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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XPathQilFactory.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.Diagnostics;
  16. using System.Xml.Schema;
  17. using System.Xml.Xsl.Qil;
  18. using System.Xml.Xsl.Runtime;
  19. namespace System.Xml.Xsl.XPath
  20. {
  21.     using Res = System.Xml.Utils.Res;
  22.     using T = XmlQueryTypeFactory;
  23.    
  24.     internal class XPathQilFactory : QilPatternFactory
  25.     {
  26.        
  27.         public XPathQilFactory(QilFactory f, bool debug) : base(f, debug)
  28.         {
  29.         }
  30.        
  31.         // Helper methods used in addition to QilPatternFactory's ones
  32.        
  33.         public QilNode Error(string res, QilNode args)
  34.         {
  35.             return Error(InvokeFormatMessage(String(res), args));
  36.         }
  37.        
  38.         public QilNode Error(ISourceLineInfo lineInfo, string res, params string[] args)
  39.         {
  40.             return Error(String(XslLoadException.CreateMessage(lineInfo, res, args)));
  41.         }
  42.        
  43.         public QilIterator FirstNode(QilNode n)
  44.         {
  45.             CheckNodeSet(n);
  46.             QilIterator i = For(DocOrderDistinct(n));
  47.             return For(Filter(i, Eq(PositionOf(i), Int32(1))));
  48.         }
  49.        
  50.         public bool IsAnyType(QilNode n)
  51.         {
  52.             XmlQueryType xt = n.XmlType;
  53.             bool result = !(xt.IsStrict || xt.IsNode);
  54.             Debug.Assert(result == (xt.TypeCode == XmlTypeCode.Item || xt.TypeCode == XmlTypeCode.AnyAtomicType), "What else can it be?");
  55.             return result;
  56.         }
  57.        
  58.         [Conditional("DEBUG")]
  59.         public void CheckAny(QilNode n)
  60.         {
  61.             Debug.Assert(n != null && IsAnyType(n), "Must be of 'any' type");
  62.         }
  63.        
  64.         [Conditional("DEBUG")]
  65.         public void CheckNode(QilNode n)
  66.         {
  67.             Debug.Assert(n != null && n.XmlType.IsSingleton && n.XmlType.IsNode, "Must be a singleton node");
  68.         }
  69.        
  70.         [Conditional("DEBUG")]
  71.         public void CheckNodeSet(QilNode n)
  72.         {
  73.             Debug.Assert(n != null && n.XmlType.IsNode, "Must be a node-set");
  74.         }
  75.        
  76.         [Conditional("DEBUG")]
  77.         public void CheckNodeNotRtf(QilNode n)
  78.         {
  79.             Debug.Assert(n != null && n.XmlType.IsSingleton && n.XmlType.IsNode && n.XmlType.IsNotRtf, "Must be a singleton node and not an Rtf");
  80.         }
  81.        
  82.         [Conditional("DEBUG")]
  83.         public void CheckString(QilNode n)
  84.         {
  85.             Debug.Assert(n != null && n.XmlType.IsSubtypeOf(T.StringX), "Must be a singleton string");
  86.         }
  87.        
  88.         [Conditional("DEBUG")]
  89.         public void CheckStringS(QilNode n)
  90.         {
  91.             Debug.Assert(n != null && n.XmlType.IsSubtypeOf(T.StringXS), "Must be a sequence of strings");
  92.         }
  93.        
  94.         [Conditional("DEBUG")]
  95.         public void CheckDouble(QilNode n)
  96.         {
  97.             Debug.Assert(n != null && n.XmlType.IsSubtypeOf(T.DoubleX), "Must be a singleton Double");
  98.         }
  99.        
  100.         [Conditional("DEBUG")]
  101.         public void CheckBool(QilNode n)
  102.         {
  103.             Debug.Assert(n != null && n.XmlType.IsSubtypeOf(T.BooleanX), "Must be a singleton Bool");
  104.         }
  105.        
  106.         // Return true if inferred type of the given expression is never a subtype of T.NodeS
  107.         public bool CannotBeNodeSet(QilNode n)
  108.         {
  109.             XmlQueryType xt = n.XmlType;
  110.             // Do not report compile error if n is a VarPar, whose inferred type forbids nodes (SQLBUDT 339398)
  111.             return xt.IsAtomicValue && !xt.IsEmpty && !(n is QilIterator);
  112.         }
  113.        
  114.         public QilNode SafeDocOrderDistinct(QilNode n)
  115.         {
  116.             XmlQueryType xt = n.XmlType;
  117.            
  118.             if (xt.MaybeMany) {
  119.                 if (xt.IsNode && xt.IsNotRtf) {
  120.                     // node-set
  121.                     return DocOrderDistinct(n);
  122.                 }
  123.                 else if (!xt.IsAtomicValue) {
  124.                     QilIterator i;
  125.                     return Loop(i = Let(n), Conditional(Gt(Length(i), Int32(1)), DocOrderDistinct(TypeAssert(i, T.NodeNotRtfS)), i));
  126.                 }
  127.             }
  128.            
  129.             return n;
  130.         }
  131.        
  132.         public QilNode InvokeFormatMessage(QilNode res, QilNode args)
  133.         {
  134.             CheckString(res);
  135.             CheckStringS(args);
  136.             return XsltInvokeEarlyBound(QName("format-message"), XsltMethods.FormatMessage, T.StringX, new QilNode[] {res, args});
  137.         }
  138.        
  139.         #region Comparisons
  140.         public QilNode InvokeEqualityOperator(QilNodeType op, QilNode left, QilNode right)
  141.         {
  142.             Debug.Assert(op == QilNodeType.Eq || op == QilNodeType.Ne);
  143.             double opCode;
  144.             left = TypeAssert(left, T.ItemS);
  145.             right = TypeAssert(right, T.ItemS);
  146.            
  147.             switch (op) {
  148.                 case QilNodeType.Eq:
  149.                     opCode = (double)XsltLibrary.ComparisonOperator.Eq;
  150.                     break;
  151.                 default:
  152.                     opCode = (double)XsltLibrary.ComparisonOperator.Ne;
  153.                     break;
  154.             }
  155.             return XsltInvokeEarlyBound(QName("EqualityOperator"), XsltMethods.EqualityOperator, T.BooleanX, new QilNode[] {Double(opCode), left, right});
  156.         }
  157.        
  158.         public QilNode InvokeRelationalOperator(QilNodeType op, QilNode left, QilNode right)
  159.         {
  160.             Debug.Assert(op == QilNodeType.Lt || op == QilNodeType.Le || op == QilNodeType.Gt || op == QilNodeType.Ge);
  161.             double opCode;
  162.             left = TypeAssert(left, T.ItemS);
  163.             right = TypeAssert(right, T.ItemS);
  164.            
  165.             switch (op) {
  166.                 case QilNodeType.Lt:
  167.                     opCode = (double)XsltLibrary.ComparisonOperator.Lt;
  168.                     break;
  169.                 case QilNodeType.Le:
  170.                     opCode = (double)XsltLibrary.ComparisonOperator.Le;
  171.                     break;
  172.                 case QilNodeType.Gt:
  173.                     opCode = (double)XsltLibrary.ComparisonOperator.Gt;
  174.                     break;
  175.                 default:
  176.                     opCode = (double)XsltLibrary.ComparisonOperator.Ge;
  177.                     break;
  178.             }
  179.             return XsltInvokeEarlyBound(QName("RelationalOperator"), XsltMethods.RelationalOperator, T.BooleanX, new QilNode[] {Double(opCode), left, right});
  180.         }
  181.         #endregion
  182.        
  183.         #region Type Conversions
  184.         [Conditional("DEBUG")]
  185.         private void ExpectAny(QilNode n)
  186.         {
  187.             Debug.Assert(IsAnyType(n), "Unexpected expression type: " + n.XmlType.ToString());
  188.         }
  189.        
  190.         public QilNode ConvertToType(XmlTypeCode requiredType, QilNode n)
  191.         {
  192.             switch (requiredType) {
  193.                 case XmlTypeCode.String:
  194.                     return ConvertToString(n);
  195.                 case XmlTypeCode.Double:
  196.                     return ConvertToNumber(n);
  197.                 case XmlTypeCode.Boolean:
  198.                     return ConvertToBoolean(n);
  199.                 case XmlTypeCode.Node:
  200.                     return EnsureNodeSet(n);
  201.                 case XmlTypeCode.Item:
  202.                     return n;
  203.                 default:
  204.                     Debug.Fail("Unexpected XmlTypeCode: " + requiredType);
  205.                     return null;
  206.             }
  207.         }
  208.        
  209.         // XPath spec $4.2, string()
  210.         public QilNode ConvertToString(QilNode n)
  211.         {
  212.             switch (n.XmlType.TypeCode) {
  213.                 case XmlTypeCode.Boolean:
  214.                         /*default: */                    return (n.NodeType == QilNodeType.True ? (QilNode)String("true") : n.NodeType == QilNodeType.False ? (QilNode)String("false") : (QilNode)Conditional(n, String("true"), String("false")));
  215.                 case XmlTypeCode.Double:
  216.                     return (n.NodeType == QilNodeType.LiteralDouble ? (QilNode)String(XPathConvert.DoubleToString((double)(QilLiteral)n)) : (QilNode)XsltConvert(n, T.StringX));
  217.                 case XmlTypeCode.String:
  218.                     return n;
  219.                 default:
  220.                     if (n.XmlType.IsNode) {
  221.                         return XPathNodeValue(SafeDocOrderDistinct(n));
  222.                     }
  223.                    
  224.                     ExpectAny(n);
  225.                     return XsltConvert(n, T.StringX);
  226.             }
  227.         }
  228.        
  229.         // XPath spec $4.3, boolean()
  230.         public QilNode ConvertToBoolean(QilNode n)
  231.         {
  232.             switch (n.XmlType.TypeCode) {
  233.                 case XmlTypeCode.Boolean:
  234.                     return n;
  235.                 case XmlTypeCode.Double:
  236.                     // (x < 0 || 0 < x) == (x != 0) && !Double.IsNaN(x)
  237.                     QilIterator i;
  238.                     return (n.NodeType == QilNodeType.LiteralDouble ? Boolean((double)(QilLiteral)n < 0 || 0 < (double)(QilLiteral)n) : Loop(i = Let(n), Or(Lt(i, Double(0)), Lt(Double(0), i))));
  239.                 case XmlTypeCode.String:
  240.                     return (n.NodeType == QilNodeType.LiteralString ? Boolean(((string)(QilLiteral)n).Length != 0) : Ne(StrLength(n), Int32(0)));
  241.                 default:
  242.                     if (n.XmlType.IsNode) {
  243.                         return Not(IsEmpty(n));
  244.                     }
  245.                    
  246.                     ExpectAny(n);
  247.                     return XsltConvert(n, T.BooleanX);
  248.             }
  249.         }
  250.        
  251.         // XPath spec $4.4, number()
  252.         public QilNode ConvertToNumber(QilNode n)
  253.         {
  254.             switch (n.XmlType.TypeCode) {
  255.                 case XmlTypeCode.Boolean:
  256.                         /*default: */                    return (n.NodeType == QilNodeType.True ? (QilNode)Double(1) : n.NodeType == QilNodeType.False ? (QilNode)Double(0) : (QilNode)Conditional(n, Double(1), Double(0)));
  257.                 case XmlTypeCode.Double:
  258.                     return n;
  259.                 case XmlTypeCode.String:
  260.                     return XsltConvert(n, T.DoubleX);
  261.                 default:
  262.                     if (n.XmlType.IsNode) {
  263.                         return XsltConvert(XPathNodeValue(SafeDocOrderDistinct(n)), T.DoubleX);
  264.                     }
  265.                    
  266.                     ExpectAny(n);
  267.                     return XsltConvert(n, T.DoubleX);
  268.             }
  269.         }
  270.        
  271.         public QilNode ConvertToNode(QilNode n)
  272.         {
  273.             if (n.XmlType.IsNode && n.XmlType.IsNotRtf && n.XmlType.IsSingleton) {
  274.                 return n;
  275.             }
  276.             return XsltConvert(n, T.NodeNotRtf);
  277.         }
  278.        
  279.         public QilNode ConvertToNodeSet(QilNode n)
  280.         {
  281.             if (n.XmlType.IsNode && n.XmlType.IsNotRtf) {
  282.                 return n;
  283.             }
  284.            
  285.             return XsltConvert(n, T.NodeNotRtfS);
  286.         }
  287.        
  288.         public QilNode EnsureNodeSet(QilNode n)
  289.         {
  290.             if (n.XmlType.IsNode && n.XmlType.IsNotRtf) {
  291.                 return n;
  292.             }
  293.             if (CannotBeNodeSet(n)) {
  294.                 throw new XPathCompileException(Res.XPath_NodeSetExpected);
  295.             }
  296.            
  297.             // Ensure it is not an Rtf at runtime
  298.             return InvokeEnsureNodeSet(n);
  299.         }
  300.        
  301.         public QilNode InvokeEnsureNodeSet(QilNode n)
  302.         {
  303.             return XsltInvokeEarlyBound(QName("ensure-node-set"), XsltMethods.EnsureNodeSet, T.NodeDodS, new QilNode[] {n});
  304.         }
  305.         #endregion
  306.        
  307.         #region Other XPath Functions
  308.         public QilNode Id(QilNode context, QilNode id)
  309.         {
  310.             CheckNodeNotRtf(context);
  311.            
  312.             if (id.XmlType.IsSingleton) {
  313.                 return Deref(context, ConvertToString(id));
  314.             }
  315.            
  316.             QilIterator i;
  317.             return Loop(i = For(id), Deref(context, ConvertToString(i)));
  318.         }
  319.        
  320.         public QilNode InvokeStartsWith(QilNode str1, QilNode str2)
  321.         {
  322.             CheckString(str1);
  323.             CheckString(str2);
  324.             return XsltInvokeEarlyBound(QName("starts-with"), XsltMethods.StartsWith, T.BooleanX, new QilNode[] {str1, str2});
  325.         }
  326.        
  327.         public QilNode InvokeContains(QilNode str1, QilNode str2)
  328.         {
  329.             CheckString(str1);
  330.             CheckString(str2);
  331.             return XsltInvokeEarlyBound(QName("contains"), XsltMethods.Contains, T.BooleanX, new QilNode[] {str1, str2});
  332.         }
  333.        
  334.         public QilNode InvokeSubstringBefore(QilNode str1, QilNode str2)
  335.         {
  336.             CheckString(str1);
  337.             CheckString(str2);
  338.             return XsltInvokeEarlyBound(QName("substring-before"), XsltMethods.SubstringBefore, T.StringX, new QilNode[] {str1, str2});
  339.         }
  340.        
  341.         public QilNode InvokeSubstringAfter(QilNode str1, QilNode str2)
  342.         {
  343.             CheckString(str1);
  344.             CheckString(str2);
  345.             return XsltInvokeEarlyBound(QName("substring-after"), XsltMethods.SubstringAfter, T.StringX, new QilNode[] {str1, str2});
  346.         }
  347.        
  348.         public QilNode InvokeSubstring(QilNode str, QilNode start)
  349.         {
  350.             CheckString(str);
  351.             CheckDouble(start);
  352.             return XsltInvokeEarlyBound(QName("substring"), XsltMethods.Substring2, T.StringX, new QilNode[] {str, start});
  353.         }
  354.        
  355.         public QilNode InvokeSubstring(QilNode str, QilNode start, QilNode length)
  356.         {
  357.             CheckString(str);
  358.             CheckDouble(start);
  359.             CheckDouble(length);
  360.             return XsltInvokeEarlyBound(QName("substring"), XsltMethods.Substring3, T.StringX, new QilNode[] {str, start, length});
  361.         }
  362.        
  363.         public QilNode InvokeNormalizeSpace(QilNode str)
  364.         {
  365.             CheckString(str);
  366.             return XsltInvokeEarlyBound(QName("normalize-space"), XsltMethods.NormalizeSpace, T.StringX, new QilNode[] {str});
  367.         }
  368.        
  369.         public QilNode InvokeTranslate(QilNode str1, QilNode str2, QilNode str3)
  370.         {
  371.             CheckString(str1);
  372.             CheckString(str2);
  373.             CheckString(str3);
  374.             return XsltInvokeEarlyBound(QName("translate"), XsltMethods.Translate, T.StringX, new QilNode[] {str1, str2, str3});
  375.         }
  376.        
  377.         public QilNode InvokeLang(QilNode lang, QilNode context)
  378.         {
  379.             CheckString(lang);
  380.             CheckNodeNotRtf(context);
  381.             return XsltInvokeEarlyBound(QName("lang"), XsltMethods.Lang, T.BooleanX, new QilNode[] {lang, context});
  382.         }
  383.        
  384.         public QilNode InvokeFloor(QilNode value)
  385.         {
  386.             CheckDouble(value);
  387.             return XsltInvokeEarlyBound(QName("floor"), XsltMethods.Floor, T.DoubleX, new QilNode[] {value});
  388.         }
  389.        
  390.         public QilNode InvokeCeiling(QilNode value)
  391.         {
  392.             CheckDouble(value);
  393.             return XsltInvokeEarlyBound(QName("ceiling"), XsltMethods.Ceiling, T.DoubleX, new QilNode[] {value});
  394.         }
  395.        
  396.         public QilNode InvokeRound(QilNode value)
  397.         {
  398.             CheckDouble(value);
  399.             return XsltInvokeEarlyBound(QName("round"), XsltMethods.Round, T.DoubleX, new QilNode[] {value});
  400.         }
  401.         #endregion
  402.     }
  403. }

Developer Fusion