The Labs \ Source Viewer \ SSCLI \ System.Xml.Xsl.XPath \ FunctionInfo

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XPathBuilder.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. using System.Collections.Generic;
  16. using System.Diagnostics;
  17. using System.Globalization;
  18. using System.Xml.Schema;
  19. using System.Xml.XPath;
  20. using System.Xml.Xsl.Qil;
  21. //#define StopMaskOptimisation
  22. namespace System.Xml.Xsl.XPath
  23. {
  24.     using FunctionInfo = XPathBuilder.FunctionInfo<XPathBuilder.FuncId>;
  25.     using Res = System.Xml.Utils.Res;
  26.     using T = XmlQueryTypeFactory;
  27.    
  28.     internal class XPathBuilder : IXPathBuilder<QilNode>, IXPathEnvironment
  29.     {
  30.         private XPathQilFactory f;
  31.         private IXPathEnvironment environment;
  32.         private bool inTheBuild;
  33.        
  34.         // Singleton nodes used as fixup markers
  35.         protected QilNode fixupCurrent, fixupPosition, fixupLast;
  36.        
  37.         // Number of unresolved fixup nodes
  38.         protected int numFixupCurrent, numFixupPosition, numFixupLast;
  39.         private FixupVisitor fixupVisitor;
  40.        
  41. /*  ----------------------------------------------------------------------------
  42.             IXPathEnvironment interface
  43.         */       
  44.         QilNode IFocus.GetCurrent()
  45.         {
  46.             return GetCurrentNode();
  47.         }
  48.         QilNode IFocus.GetPosition()
  49.         {
  50.             return GetCurrentPosition();
  51.         }
  52.         QilNode IFocus.GetLast()
  53.         {
  54.             return GetLastPosition();
  55.         }
  56.        
  57.         XPathQilFactory IXPathEnvironment.Factory {
  58.             get { return f; }
  59.         }
  60.        
  61.         QilNode IXPathEnvironment.ResolveVariable(string prefix, string name)
  62.         {
  63.             return Variable(prefix, name);
  64.         }
  65.         QilNode IXPathEnvironment.ResolveFunction(string prefix, string name, IList<QilNode> args, IFocus env)
  66.         {
  67.             Debug.Fail("Must not be called");
  68.             return null;
  69.         }
  70.         string IXPathEnvironment.ResolvePrefix(string prefix)
  71.         {
  72.             return environment.ResolvePrefix(prefix);
  73.         }
  74.         // ----------------------------------------------------------------------------
  75.        
  76.         public XPathBuilder(IXPathEnvironment environment)
  77.         {
  78.             this.environment = environment;
  79.             this.f = this.environment.Factory;
  80.             this.fixupCurrent = f.Unknown(T.NodeNotRtf);
  81.             this.fixupPosition = f.Unknown(T.DoubleX);
  82.             this.fixupLast = f.Unknown(T.DoubleX);
  83.             this.fixupVisitor = new FixupVisitor(f, fixupCurrent, fixupPosition, fixupLast);
  84.         }
  85.        
  86.         public virtual void StartBuild()
  87.         {
  88.             Debug.Assert(!inTheBuild, "XPathBuilder is busy!");
  89.             inTheBuild = true;
  90.             numFixupCurrent = numFixupPosition = numFixupLast = 0;
  91.         }
  92.        
  93.         public virtual QilNode EndBuild(QilNode result)
  94.         {
  95.             if (result == null) {
  96.                 // special door to clean builder state in exception handlers
  97.                 inTheBuild = false;
  98.                 return result;
  99.             }
  100.             Debug.Assert(inTheBuild, "StartBuild() wasn't called");
  101.             if (result.XmlType.MaybeMany && result.XmlType.IsNode && result.XmlType.IsNotRtf) {
  102.                 result = f.DocOrderDistinct(result);
  103.             }
  104.                 /*environment:*/            result = fixupVisitor.Fixup(result, this.environment);
  105.             numFixupCurrent -= fixupVisitor.numCurrent;
  106.             numFixupPosition -= fixupVisitor.numPosition;
  107.             numFixupLast -= fixupVisitor.numLast;
  108.            
  109.             // All these variables will be positive for "false() and (. = position() + last())"
  110.             // since QilPatternFactory eliminates the right operand of 'and'
  111.             Debug.Assert(numFixupCurrent >= 0, "Context fixup error");
  112.             Debug.Assert(numFixupPosition >= 0, "Context fixup error");
  113.             Debug.Assert(numFixupLast >= 0, "Context fixup error");
  114.             inTheBuild = false;
  115.             return result;
  116.         }
  117.        
  118.         private QilNode GetCurrentNode()
  119.         {
  120.             numFixupCurrent++;
  121.             return fixupCurrent;
  122.         }
  123.         private QilNode GetCurrentPosition()
  124.         {
  125.             numFixupPosition++;
  126.             return fixupPosition;
  127.         }
  128.         private QilNode GetLastPosition()
  129.         {
  130.             numFixupLast++;
  131.             return fixupLast;
  132.         }
  133.        
  134.         public virtual QilNode String(string value)
  135.         {
  136.             return f.String(value);
  137.         }
  138.        
  139.         public virtual QilNode Number(double value)
  140.         {
  141.             return f.Double(value);
  142.         }
  143.        
  144.         public virtual QilNode Operator(XPathOperator op, QilNode left, QilNode right)
  145.         {
  146.             Debug.Assert(op != XPathOperator.Unknown);
  147.             switch (OperatorGroup[(int)op]) {
  148.                 case XPathOperatorGroup.Logical:
  149.                     return LogicalOperator(op, left, right);
  150.                 case XPathOperatorGroup.Equality:
  151.                     return EqualityOperator(op, left, right);
  152.                 case XPathOperatorGroup.Relational:
  153.                     return RelationalOperator(op, left, right);
  154.                 case XPathOperatorGroup.Arithmetic:
  155.                     return ArithmeticOperator(op, left, right);
  156.                 case XPathOperatorGroup.Negate:
  157.                     return NegateOperator(op, left, right);
  158.                 case XPathOperatorGroup.Union:
  159.                     return UnionOperator(op, left, right);
  160.                 default:
  161.                     Debug.Fail(op + " is not a valid XPathOperator");
  162.                     return null;
  163.             }
  164.         }
  165.        
  166.         QilNode LogicalOperator(XPathOperator op, QilNode left, QilNode right)
  167.         {
  168.             Debug.Assert(op == XPathOperator.Or || op == XPathOperator.And);
  169.             left = f.ConvertToBoolean(left);
  170.             right = f.ConvertToBoolean(right);
  171.                 /*default*/            return (op == XPathOperator.Or ? f.Or(left, right) : f.And(left, right));
  172.         }
  173.        
  174.         QilNode CompareValues(XPathOperator op, QilNode left, QilNode right, XmlTypeCode compType)
  175.         {
  176.             Debug.Assert(compType == XmlTypeCode.Boolean || compType == XmlTypeCode.Double || compType == XmlTypeCode.String);
  177.             Debug.Assert(compType == XmlTypeCode.Boolean || left.XmlType.IsSingleton && right.XmlType.IsSingleton, "Both comparison operands must be singletons");
  178.             left = f.ConvertToType(compType, left);
  179.             right = f.ConvertToType(compType, right);
  180.            
  181.             switch (op) {
  182.                 case XPathOperator.Eq:
  183.                     return f.Eq(left, right);
  184.                 case XPathOperator.Ne:
  185.                     return f.Ne(left, right);
  186.                 case XPathOperator.Lt:
  187.                     return f.Lt(left, right);
  188.                 case XPathOperator.Le:
  189.                     return f.Le(left, right);
  190.                 case XPathOperator.Gt:
  191.                     return f.Gt(left, right);
  192.                 case XPathOperator.Ge:
  193.                     return f.Ge(left, right);
  194.                 default:
  195.                     Debug.Fail("Wrong operator type");
  196.                     return null;
  197.             }
  198.         }
  199.        
  200.         QilNode CompareNodeSetAndValue(XPathOperator op, QilNode nodeset, QilNode val, XmlTypeCode compType)
  201.         {
  202.             f.CheckNodeSet(nodeset);
  203.             Debug.Assert(val.XmlType.IsSingleton);
  204.             Debug.Assert(compType == XmlTypeCode.Boolean || compType == XmlTypeCode.Double || compType == XmlTypeCode.String, "I don't know what to do with RTF here");
  205.             if (compType == XmlTypeCode.Boolean || nodeset.XmlType.IsSingleton) {
  206.                 return CompareValues(op, nodeset, val, compType);
  207.             }
  208.             else {
  209.                 QilIterator it = f.For(nodeset);
  210.                 return f.Not(f.IsEmpty(f.Filter(it, CompareValues(op, f.XPathNodeValue(it), val, compType))));
  211.             }
  212.         }
  213.        
  214.         // Inverts relational operator in order to swap operands of the comparison
  215.         static XPathOperator InvertOp(XPathOperator op)
  216.         {
  217.                 // '<' --> '>'
  218.                 // '<=' --> '>='
  219.                 // '>' --> '<'
  220.                 // '>=' --> '<='
  221.                 /*default:*/            return (op == XPathOperator.Lt ? XPathOperator.Gt : op == XPathOperator.Le ? XPathOperator.Ge : op == XPathOperator.Gt ? XPathOperator.Lt : op == XPathOperator.Ge ? XPathOperator.Le : op);
  222.         }
  223.        
  224.         QilNode CompareNodeSetAndNodeSet(XPathOperator op, QilNode left, QilNode right, XmlTypeCode compType)
  225.         {
  226.             f.CheckNodeSet(left);
  227.             f.CheckNodeSet(right);
  228.             if (right.XmlType.IsSingleton) {
  229.                     /*nodeset:*/                    /*value:*/                return CompareNodeSetAndValue(op, left, right, compType);
  230.             }
  231.             if (left.XmlType.IsSingleton) {
  232.                 op = InvertOp(op);
  233.                     /*nodeset:*/                    /*value:*/                return CompareNodeSetAndValue(op, right, left, compType);
  234.             }
  235.             QilIterator leftEnd = f.For(left);
  236.             QilIterator rightEnd = f.For(right);
  237.             return f.Not(f.IsEmpty(f.Loop(leftEnd, f.Filter(rightEnd, CompareValues(op, f.XPathNodeValue(leftEnd), f.XPathNodeValue(rightEnd), compType)))));
  238.         }
  239.        
  240.         QilNode EqualityOperator(XPathOperator op, QilNode left, QilNode right)
  241.         {
  242.             Debug.Assert(op == XPathOperator.Eq || op == XPathOperator.Ne);
  243.             XmlQueryType leftType = left.XmlType;
  244.             XmlQueryType rightType = right.XmlType;
  245.            
  246.             if (f.IsAnyType(left) || f.IsAnyType(right)) {
  247.                 return f.InvokeEqualityOperator(QilOperator[(int)op], left, right);
  248.             }
  249.             else if (leftType.IsNode && rightType.IsNode) {
  250.                 return CompareNodeSetAndNodeSet(op, left, right, XmlTypeCode.String);
  251.             }
  252.             else if (leftType.IsNode) {
  253.                     /*nodeset:*/                    /*val:*/                return CompareNodeSetAndValue(op, left, right, rightType.TypeCode);
  254.             }
  255.             else if (rightType.IsNode) {
  256.                     /*nodeset:*/                    /*val:*/                return CompareNodeSetAndValue(op, right, left, leftType.TypeCode);
  257.             }
  258.             else {
  259.                     /*default:*/                XmlTypeCode compType = (leftType.TypeCode == XmlTypeCode.Boolean || rightType.TypeCode == XmlTypeCode.Boolean ? XmlTypeCode.Boolean : leftType.TypeCode == XmlTypeCode.Double || rightType.TypeCode == XmlTypeCode.Double ? XmlTypeCode.Double : XmlTypeCode.String);
  260.                 return CompareValues(op, left, right, compType);
  261.             }
  262.         }
  263.        
  264.         QilNode RelationalOperator(XPathOperator op, QilNode left, QilNode right)
  265.         {
  266.             Debug.Assert(op == XPathOperator.Lt || op == XPathOperator.Le || op == XPathOperator.Gt || op == XPathOperator.Ge);
  267.             XmlQueryType leftType = left.XmlType;
  268.             XmlQueryType rightType = right.XmlType;
  269.            
  270.             if (f.IsAnyType(left) || f.IsAnyType(right)) {
  271.                 return f.InvokeRelationalOperator(QilOperator[(int)op], left, right);
  272.             }
  273.             else if (leftType.IsNode && rightType.IsNode) {
  274.                 return CompareNodeSetAndNodeSet(op, left, right, XmlTypeCode.Double);
  275.             }
  276.             else if (leftType.IsNode) {
  277.                 XmlTypeCode compType = rightType.TypeCode == XmlTypeCode.Boolean ? XmlTypeCode.Boolean : XmlTypeCode.Double;
  278.                     /*nodeset:*/                    /*val:*/                return CompareNodeSetAndValue(op, left, right, compType);
  279.             }
  280.             else if (rightType.IsNode) {
  281.                 XmlTypeCode compType = leftType.TypeCode == XmlTypeCode.Boolean ? XmlTypeCode.Boolean : XmlTypeCode.Double;
  282.                 op = InvertOp(op);
  283.                     /*nodeset:*/                    /*val:*/                return CompareNodeSetAndValue(op, right, left, compType);
  284.             }
  285.             else {
  286.                 return CompareValues(op, left, right, XmlTypeCode.Double);
  287.             }
  288.         }
  289.        
  290.         QilNode NegateOperator(XPathOperator op, QilNode left, QilNode right)
  291.         {
  292.             Debug.Assert(op == XPathOperator.UnaryMinus);
  293.             Debug.Assert(right == null);
  294.             return f.Negate(f.ConvertToNumber(left));
  295.         }
  296.        
  297.         QilNode ArithmeticOperator(XPathOperator op, QilNode left, QilNode right)
  298.         {
  299.             left = f.ConvertToNumber(left);
  300.             right = f.ConvertToNumber(right);
  301.             switch (op) {
  302.                 case XPathOperator.Plus:
  303.                     return f.Add(left, right);
  304.                 case XPathOperator.Minus:
  305.                     return f.Subtract(left, right);
  306.                 case XPathOperator.Multiply:
  307.                     return f.Multiply(left, right);
  308.                 case XPathOperator.Divide:
  309.                     return f.Divide(left, right);
  310.                 case XPathOperator.Modulo:
  311.                     return f.Modulo(left, right);
  312.                 default:
  313.                     Debug.Fail("Wrong operator type");
  314.                     return null;
  315.             }
  316.         }
  317.        
  318.         QilNode UnionOperator(XPathOperator op, QilNode left, QilNode right)
  319.         {
  320.             Debug.Assert(op == XPathOperator.Union);
  321.             if (left == null) {
  322.                 return f.EnsureNodeSet(right);
  323.             }
  324.             left = f.EnsureNodeSet(left);
  325.             right = f.EnsureNodeSet(right);
  326.             if (left.NodeType == QilNodeType.Sequence) {
  327.                 ((QilList)left).Add(right);
  328.                 return left;
  329.             }
  330.             else {
  331.                 return f.Union(left, right);
  332.             }
  333.         }
  334.        
  335.         // also called by XPathPatternBuilder
  336.         public static XmlNodeKindFlags AxisTypeMask(XmlNodeKindFlags inputTypeMask, XPathNodeType nodeType, XPathAxis xpathAxis)
  337.         {
  338.             return (XmlNodeKindFlags)((int)inputTypeMask & (int)XPathNodeType2QilXmlNodeKind[(int)nodeType] & (int)XPathAxisMask[(int)xpathAxis]);
  339.         }
  340.        
  341.         QilNode BuildAxisFilter(QilNode qilAxis, XPathAxis xpathAxis, XPathNodeType nodeType, string name, string nsUri)
  342.         {
  343.             XmlNodeKindFlags original = qilAxis.XmlType.NodeKinds;
  344.             XmlNodeKindFlags required = AxisTypeMask(original, nodeType, xpathAxis);
  345.            
  346.             QilIterator itr;
  347.            
  348.             if (required == 0) {
  349.                 return f.Sequence();
  350.             }
  351.             else if (required == original) {
  352.             }
  353.             else {
  354.                 qilAxis = f.Filter(itr = f.For(qilAxis), f.IsType(itr, T.NodeChoice(required)));
  355.                 qilAxis.XmlType = T.PrimeProduct(T.NodeChoice(required), qilAxis.XmlType.Cardinality);
  356.                
  357.                
  358.                
  359.                 if (qilAxis.NodeType == QilNodeType.Filter) {
  360.                     QilLoop filter = (QilLoop)qilAxis;
  361.                         // ns:bar || bar
  362.                         // ns:*
  363.                         // *:foo
  364.                         /*name  == nsUri == null*/                        // *
  365.                     filter.Body = f.And(filter.Body, name != null && nsUri != null ? f.Eq(f.NameOf(itr), f.QName(name, nsUri)) : nsUri != null ? f.Eq(f.NamespaceUriOf(itr), f.String(nsUri)) : name != null ? f.Eq(f.LocalNameOf(itr), f.String(name)) : f.True());
  366.                     return filter;
  367.                 }
  368.             }
  369.            
  370.                 // ns:bar || bar
  371.                 // ns:*
  372.                 // *:foo
  373.                 /*name  == nsUri == null*/                // *
  374.             return f.Filter(itr = f.For(qilAxis), name != null && nsUri != null ? f.Eq(f.NameOf(itr), f.QName(name, nsUri)) : nsUri != null ? f.Eq(f.NamespaceUriOf(itr), f.String(nsUri)) : name != null ? f.Eq(f.LocalNameOf(itr), f.String(name)) : f.True());
  375.         }
  376.        
  377.         // XmlNodeKindFlags from XPathNodeType
  378.         static XmlNodeKindFlags[] XPathNodeType2QilXmlNodeKind = {XmlNodeKindFlags.Document, XmlNodeKindFlags.Element, XmlNodeKindFlags.Attribute, XmlNodeKindFlags.Namespace, XmlNodeKindFlags.Text, XmlNodeKindFlags.Text, XmlNodeKindFlags.Text, XmlNodeKindFlags.PI, XmlNodeKindFlags.Comment, XmlNodeKindFlags.Any
  379.             /*Root                */            /*Element              */            /*Attribute            */            /*Namespace            */            /*Text                */            /*SignificantWhitespace*/            /*Whitespace          */            /*ProcessingInstruction*/            /*Comment              */            /*All                  */        };
  380.        
  381.         QilNode BuildAxis(XPathAxis xpathAxis, XPathNodeType nodeType, string nsUri, string name)
  382.         {
  383.             QilNode currentNode = GetCurrentNode();
  384.             QilNode qilAxis;
  385.            
  386.             switch (xpathAxis) {
  387.                 case XPathAxis.Ancestor:
  388.                     qilAxis = f.Ancestor(currentNode);
  389.                     break;
  390.                 case XPathAxis.AncestorOrSelf:
  391.                     qilAxis = f.AncestorOrSelf(currentNode);
  392.                     break;
  393.                 case XPathAxis.Attribute:
  394.                     qilAxis = f.Content(currentNode);
  395.                     break;
  396.                 case XPathAxis.Child:
  397.                     qilAxis = f.Content(currentNode);
  398.                     break;
  399.                 case XPathAxis.Descendant:
  400.                     qilAxis = f.Descendant(currentNode);
  401.                     break;
  402.                 case XPathAxis.DescendantOrSelf:
  403.                     qilAxis = f.DescendantOrSelf(currentNode);
  404.                     break;
  405.                 case XPathAxis.Following:
  406.                     qilAxis = f.XPathFollowing(currentNode);
  407.                     break;
  408.                 case XPathAxis.FollowingSibling:
  409.                     qilAxis = f.FollowingSibling(currentNode);
  410.                     break;
  411.                 case XPathAxis.Namespace:
  412.                     qilAxis = f.XPathNamespace(currentNode);
  413.                     break;
  414.                 case XPathAxis.Parent:
  415.                     qilAxis = f.Parent(currentNode);
  416.                     break;
  417.                 case XPathAxis.Preceding:
  418.                     qilAxis = f.XPathPreceding(currentNode);
  419.                     break;
  420.                 case XPathAxis.PrecedingSibling:
  421.                     qilAxis = f.PrecedingSibling(currentNode);
  422.                     break;
  423.                 case XPathAxis.Self:
  424.                     qilAxis = (currentNode);
  425.                     break;
  426.                 case XPathAxis.Root:
  427.                     return f.Root(currentNode);
  428.                 default:
  429.                     qilAxis = null;
  430.                     Debug.Fail("Invalid EnumValue 'XPathAxis'");
  431.                     break;
  432.             }
  433.            
  434.             QilNode result = BuildAxisFilter(qilAxis, xpathAxis, nodeType, name, nsUri);
  435.             if (xpathAxis == XPathAxis.Ancestor || xpathAxis == XPathAxis.Preceding || xpathAxis == XPathAxis.AncestorOrSelf || xpathAxis == XPathAxis.PrecedingSibling) {
  436.                 result = f.BaseFactory.DocOrderDistinct(result);
  437.                 // To make grouping operator NOP we should always return path expressions in DOD.
  438.                 // I can't use Pattern factory here becasue Predicate() depends on fact that DOD() is
  439.                 // outmost node in reverse steps
  440.             }
  441.             return result;
  442.         }
  443.        
  444.         public virtual QilNode Axis(XPathAxis xpathAxis, XPathNodeType nodeType, string prefix, string name)
  445.         {
  446.             string nsUri = prefix == null ? null : this.environment.ResolvePrefix(prefix);
  447.             return BuildAxis(xpathAxis, nodeType, nsUri, name);
  448.         }
  449.        
  450.         // "left/right"
  451.         public virtual QilNode JoinStep(QilNode left, QilNode right)
  452.         {
  453.             f.CheckNodeSet(right);
  454.             QilIterator leftIt = f.For(f.EnsureNodeSet(left));
  455.             // in XPath 1.0 step is always nodetest and as a result it can't contain last().
  456.                 /*current:*/                /*last:*/            right = fixupVisitor.Fixup(right, leftIt, null);
  457.             numFixupCurrent -= fixupVisitor.numCurrent;
  458.             numFixupPosition -= fixupVisitor.numPosition;
  459.             numFixupLast -= fixupVisitor.numLast;
  460.             return f.DocOrderDistinct(f.Loop(leftIt, right));
  461.         }
  462.        
  463.         // "nodeset[predicate]"
  464.         // XPath spec $3.3 (para 5)
  465.         public virtual QilNode Predicate(QilNode nodeset, QilNode predicate, bool isReverseStep)
  466.         {
  467.             if (isReverseStep) {
  468.                 Debug.Assert(nodeset.NodeType == QilNodeType.DocOrderDistinct, "ReverseAxe in Qil is actuly reverse and we compile them here in builder by wrapping to DocOrderDistinct()");
  469.                 // The trick here is that we unwarp it back, compile as regular predicate and wrap again.
  470.                 // this way this wat we hold invariant that path expresion are always DOD and make predicates on reverse axe
  471.                 // work as specified in XPath 2.0 FS: http://www.w3.org/TR/xquery-semantics/#id-axis-steps
  472.                 nodeset = ((QilUnary)nodeset).Child;
  473.             }
  474.            
  475.             nodeset = f.EnsureNodeSet(nodeset);
  476.            
  477.             // Prepocess predicate: if (predicate is number) then predicate := (position() == predicate)
  478.             if (!f.IsAnyType(predicate)) {
  479.                 if (predicate.XmlType.TypeCode == XmlTypeCode.Double) {
  480.                     predicate = f.Eq(GetCurrentPosition(), predicate);
  481.                 }
  482.                 else {
  483.                     predicate = f.ConvertToBoolean(predicate);
  484.                 }
  485.             }
  486.             else {
  487.                 QilIterator i;
  488.                 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)));
  489.             }
  490.            
  491.            
  492.             QilNode result;
  493.             if (numFixupLast != 0 && fixupVisitor.CountUnfixedLast(predicate) != 0) {
  494.                 // this subtree has unfixed last() nodes
  495.                 QilIterator cash = f.Let(nodeset);
  496.                 QilIterator size = f.Let(f.XsltConvert(f.Length(cash), T.DoubleX));
  497.                 QilIterator it = f.For(cash);
  498.                     /*current:*/                    /*last:*/                predicate = fixupVisitor.Fixup(predicate, it, size);
  499.                 numFixupCurrent -= fixupVisitor.numCurrent;
  500.                 numFixupPosition -= fixupVisitor.numPosition;
  501.                 numFixupLast -= fixupVisitor.numLast;
  502.                 result = f.Loop(cash, f.Loop(size, f.Filter(it, predicate)));
  503.             }
  504.             else {
  505.                 QilIterator it = f.For(nodeset);
  506.                     /*current:*/                    /*last:*/                predicate = fixupVisitor.Fixup(predicate, it, null);
  507.                 numFixupCurrent -= fixupVisitor.numCurrent;
  508.                 numFixupPosition -= fixupVisitor.numPosition;
  509.                 numFixupLast -= fixupVisitor.numLast;
  510.                 result = f.Filter(it, predicate);
  511.             }
  512.             if (isReverseStep) {
  513.                 result = f.DocOrderDistinct(result);
  514.             }
  515.             return result;
  516.         }
  517.        
  518.         public virtual QilNode Variable(string prefix, string name)
  519.         {
  520.             return this.environment.ResolveVariable(prefix, name);
  521.         }
  522.        
  523.         public virtual QilNode Function(string prefix, string name, IList<QilNode> args)
  524.         {
  525.             Debug.Assert(!args.IsReadOnly, "Writable collection expected");
  526.             if (prefix.Length == 0) {
  527.                 FunctionInfo func;
  528.                 if (FunctionTable.TryGetValue(name, out func)) {
  529.                     func.CastArguments(args, name, f);
  530.                    
  531.                     switch (func.id) {
  532.                         case FuncId.Not:
  533.                             return f.Not(args[0]);
  534.                         case FuncId.Last:
  535.                             return GetLastPosition();
  536.                         case FuncId.Position:
  537.                             return GetCurrentPosition();
  538.                         case FuncId.Count:
  539.                             return f.XsltConvert(f.Length(f.DocOrderDistinct(args[0])), T.DoubleX);
  540.                         case FuncId.LocalName:
  541.                             return args.Count == 0 ? f.LocalNameOf(GetCurrentNode()) : LocalNameOfFirstNode(args[0]);
  542.                         case FuncId.NamespaceUri:
  543.                             return args.Count == 0 ? f.NamespaceUriOf(GetCurrentNode()) : NamespaceOfFirstNode(args[0]);
  544.                         case FuncId.Name:
  545.                             return args.Count == 0 ? NameOf(GetCurrentNode()) : NameOfFirstNode(args[0]);
  546.                         case FuncId.String:
  547.                             return args.Count == 0 ? f.XPathNodeValue(GetCurrentNode()) : f.ConvertToString(args[0]);
  548.                         case FuncId.Number:
  549.                             return args.Count == 0 ? f.XsltConvert(f.XPathNodeValue(GetCurrentNode()), T.DoubleX) : f.ConvertToNumber(args[0]);
  550.                         case FuncId.Boolean:
  551.                             return f.ConvertToBoolean(args[0]);
  552.                         case FuncId.True:
  553.                             return f.True();
  554.                         case FuncId.False:
  555.                             return f.False();
  556.                         case FuncId.Id:
  557.                             return f.DocOrderDistinct(f.Id(GetCurrentNode(), args[0]));
  558.                         case FuncId.Concat:
  559.                             return f.StrConcat(args);
  560.                         case FuncId.StartsWith:
  561.                             return f.InvokeStartsWith(args[0], args[1]);
  562.                         case FuncId.Contains:
  563.                             return f.InvokeContains(args[0], args[1]);
  564.                         case FuncId.SubstringBefore:
  565.                             return f.InvokeSubstringBefore(args[0], args[1]);
  566.                         case FuncId.SubstringAfter:
  567.                             return f.InvokeSubstringAfter(args[0], args[1]);
  568.                         case FuncId.Substring:
  569.                             return args.Count == 2 ? f.InvokeSubstring(args[0], args[1]) : f.InvokeSubstring(args[0], args[1], args[2]);
  570.                         case FuncId.StringLength:
  571.                             return f.XsltConvert(f.StrLength(args.Count == 0 ? f.XPathNodeValue(GetCurrentNode()) : args[0]), T.DoubleX);
  572.                         case FuncId.Normalize:
  573.                             return f.InvokeNormalizeSpace(args.Count == 0 ? f.XPathNodeValue(GetCurrentNode()) : args[0]);
  574.                         case FuncId.Translate:
  575.                             return f.InvokeTranslate(args[0], args[1], args[2]);
  576.                         case FuncId.Lang:
  577.                             return f.InvokeLang(args[0], GetCurrentNode());
  578.                         case FuncId.Sum:
  579.                             return Sum(f.DocOrderDistinct(args[0]));
  580.                         case FuncId.Floor:
  581.                             return f.InvokeFloor(args[0]);
  582.                         case FuncId.Ceiling:
  583.                             return f.InvokeCeiling(args[0]);
  584.                         case FuncId.Round:
  585.                             return f.InvokeRound(args[0]);
  586.                         default:
  587.                             Debug.Fail(func.id + " is present in the function table, but absent from the switch");
  588.                             return null;
  589.                     }
  590.                 }
  591.             }
  592.            
  593.             return this.environment.ResolveFunction(prefix, name, args, (IFocus)this);
  594.         }
  595.        
  596.         QilNode LocalNameOfFirstNode(QilNode arg)
  597.         {
  598.             f.CheckNodeSet(arg);
  599.             if (arg.XmlType.IsSingleton) {
  600.                 return f.LocalNameOf(arg);
  601.             }
  602.             else {
  603.                 QilIterator i;
  604.                 return f.StrConcat(f.Loop(i = f.FirstNode(arg), f.LocalNameOf(i)));
  605.             }
  606.         }
  607.        
  608.         QilNode NamespaceOfFirstNode(QilNode arg)
  609.         {
  610.             f.CheckNodeSet(arg);
  611.             if (arg.XmlType.IsSingleton) {
  612.                 return f.NamespaceUriOf(arg);
  613.             }
  614.             else {
  615.                 QilIterator i;
  616.                 return f.StrConcat(f.Loop(i = f.FirstNode(arg), f.NamespaceUriOf(i)));
  617.             }
  618.         }
  619.        
  620.         QilNode NameOf(QilNode arg)
  621.         {
  622.             f.CheckNodeNotRtf(arg);
  623.             if (arg is QilIterator) {
  624.                 // We use arg in several places here and can do this only if arg is QilIterator.
  625.                 return f.Conditional(f.Eq(f.StrLength(f.PrefixOf(arg)), f.Int32(0)), f.LocalNameOf(arg), f.StrConcat(f.PrefixOf(arg), f.String(":"), f.LocalNameOf(arg)));
  626.             }
  627.             else {
  628.                 QilIterator let = f.Let(arg);
  629.                     /*recursion:*/                return f.Loop(let, NameOf(let));
  630.             }
  631.         }
  632.        
  633.         QilNode NameOfFirstNode(QilNode arg)
  634.         {
  635.             f.CheckNodeSet(arg);
  636.             if (arg.XmlType.IsSingleton) {
  637.                 return NameOf(arg);
  638.             }
  639.             else {
  640.                 QilIterator i;
  641.                 return f.StrConcat(f.Loop(i = f.FirstNode(arg), NameOf(i)));
  642.             }
  643.         }
  644.        
  645.         QilNode Sum(QilNode arg)
  646.         {
  647.             f.CheckNodeSet(arg);
  648.             QilIterator i;
  649.             return f.Sum(f.Sequence(f.Double(0.0), f.Loop(i = f.For(arg), f.ConvertToNumber(i))));
  650.         }
  651.        
  652.         enum XPathOperatorGroup
  653.         {
  654.             Unknown,
  655.             Logical,
  656.             Equality,
  657.             Relational,
  658.             Arithmetic,
  659.             Negate,
  660.             Union
  661.         }
  662.        
  663.         static XPathOperatorGroup[] OperatorGroup = {XPathOperatorGroup.Unknown, XPathOperatorGroup.Logical, XPathOperatorGroup.Logical, XPathOperatorGroup.Equality, XPathOperatorGroup.Equality, XPathOperatorGroup.Relational, XPathOperatorGroup.Relational, XPathOperatorGroup.Relational, XPathOperatorGroup.Relational, XPathOperatorGroup.Arithmetic,
  664.             /*Unknown  */            /*Or        */            /*And      */            /*Eq        */            /*Ne        */            /*Lt        */            /*Le        */            /*Gt        */            /*Ge        */            /*Plus      */            /*Minus    */            /*Multiply  */            /*Divide    */            /*Modulo    */            /*UnaryMinus*/            /*Union    */        XPathOperatorGroup.Arithmetic, XPathOperatorGroup.Arithmetic, XPathOperatorGroup.Arithmetic, XPathOperatorGroup.Arithmetic, XPathOperatorGroup.Negate, XPathOperatorGroup.Union};
  665.        
  666.         static QilNodeType[] QilOperator = {QilNodeType.Unknown, QilNodeType.Or, QilNodeType.And, QilNodeType.Eq, QilNodeType.Ne, QilNodeType.Lt, QilNodeType.Le, QilNodeType.Gt, QilNodeType.Ge, QilNodeType.Add,
  667.             /*Unknown    */            /*Or        */            /*And        */            /*Eq        */            /*Ne        */            /*Lt        */            /*Le        */            /*Gt        */            /*Ge        */            /*Plus      */            /*Minus      */            /*Multiply  */            /*Divide    */            /*Modulo    */            /*UnaryMinus */            /*Union      */        QilNodeType.Subtract, QilNodeType.Multiply, QilNodeType.Divide, QilNodeType.Modulo, QilNodeType.Negate, QilNodeType.Sequence};
  668.        
  669.         // XmlNodeType(s) of nodes by XPathAxis
  670.         static XmlNodeKindFlags[] XPathAxisMask = {XmlNodeKindFlags.None, XmlNodeKindFlags.Element | XmlNodeKindFlags.Document, XmlNodeKindFlags.Any, XmlNodeKindFlags.Attribute, XmlNodeKindFlags.Content, XmlNodeKindFlags.Content, XmlNodeKindFlags.Any, XmlNodeKindFlags.Content, XmlNodeKindFlags.Content, XmlNodeKindFlags.Namespace,
  671.             /*Unknown        */            /*Ancestor        */            /*AncestorOrSelf  */            /*Attribute      */            /*Child          */            /*Descendant      */            /*DescendantOrSelf*/            /*Following      */            /*FollowingSibling*/            /*Namespace      */            /*Parent          */            /*Preceding      */            /*PrecedingSibling*/            /*Self            */            /*Root            */        XmlNodeKindFlags.Element | XmlNodeKindFlags.Document, XmlNodeKindFlags.Content, XmlNodeKindFlags.Content, XmlNodeKindFlags.Any, XmlNodeKindFlags.Element | XmlNodeKindFlags.Document};
  672.        
  673.         // ----------------------------------------------------------------
  674.         internal enum FuncId
  675.         {
  676.             Last = 0,
  677.             Position,
  678.             Count,
  679.             LocalName,
  680.             NamespaceUri,
  681.             Name,
  682.             String,
  683.             Number,
  684.             Boolean,
  685.             True,
  686.             False,
  687.             Not,
  688.             Id,
  689.             Concat,
  690.             StartsWith,
  691.             Contains,
  692.             SubstringBefore,
  693.             SubstringAfter,
  694.             Substring,
  695.             StringLength,
  696.             Normalize,
  697.             Translate,
  698.             Lang,
  699.             Sum,
  700.             Floor,
  701.             Ceiling,
  702.             Round
  703.         }
  704.        
  705.         public static readonly XmlTypeCode[] argAny = {XmlTypeCode.Item};
  706.         public static readonly XmlTypeCode[] argNodeSet = {XmlTypeCode.Node};
  707.         public static readonly XmlTypeCode[] argBoolean = {XmlTypeCode.Boolean};
  708.         public static readonly XmlTypeCode[] argDouble = {XmlTypeCode.Double};
  709.         public static readonly XmlTypeCode[] argString = {XmlTypeCode.String};
  710.         public static readonly XmlTypeCode[] argString2 = {XmlTypeCode.String, XmlTypeCode.String};
  711.         public static readonly XmlTypeCode[] argString3 = {XmlTypeCode.String, XmlTypeCode.String, XmlTypeCode.String};
  712.         public static readonly XmlTypeCode[] argFnSubstr = {XmlTypeCode.String, XmlTypeCode.Double, XmlTypeCode.Double};
  713.        
  714.         public static Dictionary<string, FunctionInfo> FunctionTable = CreateFunctionTable();
  715.         private static Dictionary<string, FunctionInfo> CreateFunctionTable()
  716.         {
  717.             Dictionary<string, FunctionInfo> table = new Dictionary<string, FunctionInfo>(36);
  718.             table.Add("last", new FunctionInfo(FuncId.Last, 0, 0, null));
  719.             table.Add("position", new FunctionInfo(FuncId.Position, 0, 0, null));
  720.             table.Add("name", new FunctionInfo(FuncId.Name, 0, 1, argNodeSet));
  721.             table.Add("namespace-uri", new FunctionInfo(FuncId.NamespaceUri, 0, 1, argNodeSet));
  722.             table.Add("local-name", new FunctionInfo(FuncId.LocalName, 0, 1, argNodeSet));
  723.             table.Add("count", new FunctionInfo(FuncId.Count, 1, 1, argNodeSet));
  724.             table.Add("id", new FunctionInfo(FuncId.Id, 1, 1, argAny));
  725.             table.Add("string", new FunctionInfo(FuncId.String, 0, 1, argAny));
  726.             table.Add("concat", new FunctionInfo(FuncId.Concat, 2, FunctionInfo.Infinity, null));
  727.             table.Add("starts-with", new FunctionInfo(FuncId.StartsWith, 2, 2, argString2));
  728.             table.Add("contains", new FunctionInfo(FuncId.Contains, 2, 2, argString2));
  729.             table.Add("substring-before", new FunctionInfo(FuncId.SubstringBefore, 2, 2, argString2));
  730.             table.Add("substring-after", new FunctionInfo(FuncId.SubstringAfter, 2, 2, argString2));
  731.             table.Add("substring", new FunctionInfo(FuncId.Substring, 2, 3, argFnSubstr));
  732.             table.Add("string-length", new FunctionInfo(FuncId.StringLength, 0, 1, argString));
  733.             table.Add("normalize-space", new FunctionInfo(FuncId.Normalize, 0, 1, argString));
  734.             table.Add("translate", new FunctionInfo(FuncId.Translate, 3, 3, argString3));
  735.             table.Add("boolean", new FunctionInfo(FuncId.Boolean, 1, 1, argAny));
  736.             table.Add("not", new FunctionInfo(FuncId.Not, 1, 1, argBoolean));
  737.             table.Add("true", new FunctionInfo(FuncId.True, 0, 0, null));
  738.             table.Add("false", new FunctionInfo(FuncId.False, 0, 0, null));
  739.             table.Add("lang", new FunctionInfo(FuncId.Lang, 1, 1, argString));
  740.             table.Add("number", new FunctionInfo(FuncId.Number, 0, 1, argAny));
  741.             table.Add("sum", new FunctionInfo(FuncId.Sum, 1, 1, argNodeSet));
  742.             table.Add("floor", new FunctionInfo(FuncId.Floor, 1, 1, argDouble));
  743.             table.Add("ceiling", new FunctionInfo(FuncId.Ceiling, 1, 1, argDouble));
  744.             table.Add("round", new FunctionInfo(FuncId.Round, 1, 1, argDouble));
  745.             return table;
  746.         }
  747.        
  748.         public static bool IsFunctionAvailable(string localName, string nsUri)
  749.         {
  750.             if (nsUri.Length != 0) {
  751.                 return false;
  752.             }
  753.             return FunctionTable.ContainsKey(localName);
  754.         }
  755.        
  756.         private class FixupVisitor : QilReplaceVisitor
  757.         {
  758.             new QilPatternFactory f;
  759.             QilNode fixupCurrent, fixupPosition, fixupLast;
  760.             // fixup nodes we are replacing
  761.             QilIterator current;
  762.             QilNode last;
  763.             // expressions we are using to replace fixupNodes
  764.             bool justCount;
  765.             // Don't change tree, just count
  766.             IXPathEnvironment environment;
  767.             // temp solution
  768.             public int numCurrent, numPosition, numLast;
  769.             // here we are counting all replacements we have made
  770.             public FixupVisitor(QilPatternFactory f, QilNode fixupCurrent, QilNode fixupPosition, QilNode fixupLast) : base(f.BaseFactory)
  771.             {
  772.                 this.f = f;
  773.                 this.fixupCurrent = fixupCurrent;
  774.                 this.fixupPosition = fixupPosition;
  775.                 this.fixupLast = fixupLast;
  776.             }
  777.            
  778.             public QilNode Fixup(QilNode inExpr, QilIterator current, QilNode last)
  779.             {
  780.                 this.current = current;
  781.                 this.last = last;
  782.                 Debug.Assert(current != null);
  783.                 this.justCount = false;
  784.                 this.environment = null;
  785.                 numCurrent = numPosition = numLast = 0;
  786.                 inExpr = VisitAssumeReference(inExpr);
  787.                 #if StopMaskOptimisation
  788.                     /*stop*/                SetStopVisitMark(inExpr, true);
  789.                 #endif
  790.                 return inExpr;
  791.             }
  792.            
  793.             public QilNode Fixup(QilNode inExpr, IXPathEnvironment environment)
  794.             {
  795.                 Debug.Assert(environment != null);
  796.                 this.justCount = false;
  797.                 this.current = null;
  798.                 this.environment = environment;
  799.                 numCurrent = numPosition = numLast = 0;
  800.                 inExpr = VisitAssumeReference(inExpr);
  801.                 #if StopMaskOptimisation
  802.                 // Don't need
  803.                 //SetStopVisitMark(inExpr, /*stop*/true);
  804.                 #endif
  805.                 return inExpr;
  806.             }
  807.            
  808.             public int CountUnfixedLast(QilNode inExpr)
  809.             {
  810.                 this.justCount = true;
  811.                 numCurrent = numPosition = numLast = 0;
  812.                 VisitAssumeReference(inExpr);
  813.                 return numLast;
  814.             }
  815.            
  816.             protected override QilNode VisitUnknown(QilNode unknown)
  817.             {
  818.                 Debug.Assert(unknown.NodeType == QilNodeType.Unknown);
  819.                 if (unknown == fixupCurrent) {
  820.                     numCurrent++;
  821.                     if (!justCount) {
  822.                         if (this.environment != null) {
  823.                             unknown = this.environment.GetCurrent();
  824.                         }
  825.                         else if (this.current != null) {
  826.                             unknown = this.current;
  827.                         }
  828.                     }
  829.                 }
  830.                 else if (unknown == fixupPosition) {
  831.                     numPosition++;
  832.                     if (!justCount) {
  833.                         if (this.environment != null) {
  834.                             unknown = this.environment.GetPosition();
  835.                         }
  836.                         else if (this.current != null) {
  837.                             // position can be in predicate only and in predicate current olways an iterator
  838.                             unknown = f.XsltConvert(f.PositionOf((QilIterator)this.current), T.DoubleX);
  839.                         }
  840.                     }
  841.                 }
  842.                 else if (unknown == fixupLast) {
  843.                     numLast++;
  844.                     if (!justCount) {
  845.                         if (this.environment != null) {
  846.                             unknown = this.environment.GetLast();
  847.                         }
  848.                         else if (this.current != null) {
  849.                             Debug.Assert(this.last != null);
  850.                             unknown = this.last;
  851.                         }
  852.                     }
  853.                 }
  854.                 Debug.Assert(unknown != null);
  855.                 return unknown;
  856.             }
  857.            
  858.             #if StopMaskOptimisation
  859.             // This optimisation marks subtrees that was fixed already and prevents FixupVisitor from
  860.             // visiting them again. The logic is brokken, because when unfixed tree is added inside fixed one
  861.             // it never fixed anymore.
  862.             // This happens in all cortasian productions now.
  863.             // Excample "a/b=c". 'c' is added inside 'b'
  864.            
  865.             // I belive some optimisation is posible and would be nice to have.
  866.             // We may change the way we generating cortasian product.
  867.            
  868.             protected override QilNode Visit(QilNode n)
  869.             {
  870.                 if (GetStopVisitMark(n)) {
  871.                     // Optimisation:
  872.                     // This subtree was fixed already. No need to go inside it.
  873.                     if (!justCount) {
  874.                             /*stop*/                        SetStopVisitMark(n, false);
  875.                         // We clean this annotation
  876.                     }
  877.                     return n;
  878.                 }
  879.                 return base.Visit(n);
  880.             }
  881.            
  882.             void SetStopVisitMark(QilNode n, bool stop)
  883.             {
  884.                 if (n.Type != QilNodeType.For && n.Type != QilNodeType.Let) {
  885.                         /*any object*/                    XsltAnnotation.Write(n)[0] = (stop ? fixupCurrent : null);
  886.                 }
  887.                 else {
  888.                     // we shouldn't alter annotation of "reference" nodes (Iterators, Functions, ...)
  889.                 }
  890.             }
  891.             bool GetStopVisitMark(QilNode n)
  892.             {
  893.                 return XsltAnnotation.Write(n)[0] != null;
  894.             }
  895.             #endif
  896.         }
  897.        
  898.         internal class FunctionInfo<T>
  899.         {
  900.             public T id;
  901.             public int minArgs;
  902.             public int maxArgs;
  903.             public XmlTypeCode[] argTypes;
  904.            
  905.             public const int Infinity = int.MaxValue;
  906.            
  907.             public FunctionInfo(T id, int minArgs, int maxArgs, XmlTypeCode[] argTypes)
  908.             {
  909.                 Debug.Assert(maxArgs == 0 || maxArgs == Infinity || argTypes != null && argTypes.Length == maxArgs);
  910.                 this.id = id;
  911.                 this.minArgs = minArgs;
  912.                 this.maxArgs = maxArgs;
  913.                 this.argTypes = argTypes;
  914.             }
  915.            
  916.             public static void CheckArity(int minArgs, int maxArgs, string name, int numArgs)
  917.             {
  918.                 if (minArgs <= numArgs && numArgs <= maxArgs) {
  919.                     return;
  920.                 }
  921.                
  922.                 // Possible cases:
  923.                 // [0, 0], [1, 1], [2, 2], [3, 3]
  924.                 // [0, 1], [1, 2], [2, 3], [2, +inf]
  925.                 // [1, 3], [2, 4]
  926.                 string resId;
  927.                 if (minArgs == maxArgs) {
  928.                     resId = Res.XPath_NArgsExpected;
  929.                 }
  930.                 else {
  931.                     if (maxArgs == minArgs + 1) {
  932.                         resId = Res.XPath_NOrMArgsExpected;
  933.                     }
  934.                     else if (numArgs < minArgs) {
  935.                         resId = Res.XPath_AtLeastNArgsExpected;
  936.                     }
  937.                     else {
  938.                         // This case is impossible for standard XPath/XSLT functions
  939.                         Debug.Assert(numArgs > maxArgs);
  940.                         resId = Res.XPath_AtMostMArgsExpected;
  941.                     }
  942.                 }
  943.                 throw new XPathCompileException(resId, name, minArgs.ToString(CultureInfo.InvariantCulture), maxArgs.ToString(CultureInfo.InvariantCulture));
  944.             }
  945.            
  946.             public void CastArguments(IList<QilNode> args, string name, XPathQilFactory f)
  947.             {
  948.                 CheckArity(this.minArgs, this.maxArgs, name, args.Count);
  949.                
  950.                 // Convert arguments to the appropriate types
  951.                 if (maxArgs == Infinity) {
  952.                     // Special case for concat() function
  953.                     for (int i = 0; i < args.Count; i++) {
  954.                         args[i] = f.ConvertToType(XmlTypeCode.String, args[i]);
  955.                     }
  956.                 }
  957.                 else {
  958.                     for (int i = 0; i < args.Count; i++) {
  959.                         if (argTypes[i] == XmlTypeCode.Node && f.CannotBeNodeSet(args[i])) {
  960.                             throw new XPathCompileException(Res.XPath_NodeSetArgumentExpected, name, (i + 1).ToString(CultureInfo.InvariantCulture));
  961.                         }
  962.                         args[i] = f.ConvertToType(argTypes[i], args[i]);
  963.                     }
  964.                 }
  965.             }
  966.         }
  967.     }
  968. }

Developer Fusion