The Labs \ Source Viewer \ SSCLI \ System.Xml.Xsl.Runtime \ ComparisonOperator

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XsltLibrary.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.Specialized;
  16. using System.Collections.Generic;
  17. using System.Diagnostics;
  18. using System.Globalization;
  19. using System.Reflection;
  20. using System.Xml.XPath;
  21. using System.Xml.Xsl.Xslt;
  22. using System.ComponentModel;
  23. namespace System.Xml.Xsl.Runtime
  24. {
  25.     using Res = System.Xml.Utils.Res;
  26.    
  27.     // List of all XPath/XSLT runtime methods
  28.     static internal class XsltMethods
  29.     {
  30.         // Formatting error messages
  31.         public static readonly MethodInfo FormatMessage = GetMethod(typeof(XsltLibrary), "FormatMessage");
  32.        
  33.         // Runtime type checks and casts
  34.         public static readonly MethodInfo EnsureNodeSet = GetMethod(typeof(XsltConvert), "EnsureNodeSet", typeof(IList<XPathItem>));
  35.        
  36.         // Comparisons
  37.         public static readonly MethodInfo EqualityOperator = GetMethod(typeof(XsltLibrary), "EqualityOperator");
  38.         public static readonly MethodInfo RelationalOperator = GetMethod(typeof(XsltLibrary), "RelationalOperator");
  39.        
  40.         // XPath functions
  41.         public static readonly MethodInfo StartsWith = GetMethod(typeof(XsltFunctions), "StartsWith");
  42.         public static readonly MethodInfo Contains = GetMethod(typeof(XsltFunctions), "Contains");
  43.         public static readonly MethodInfo SubstringBefore = GetMethod(typeof(XsltFunctions), "SubstringBefore");
  44.         public static readonly MethodInfo SubstringAfter = GetMethod(typeof(XsltFunctions), "SubstringAfter");
  45.         public static readonly MethodInfo Substring2 = GetMethod(typeof(XsltFunctions), "Substring", typeof(string), typeof(double));
  46.         public static readonly MethodInfo Substring3 = GetMethod(typeof(XsltFunctions), "Substring", typeof(string), typeof(double), typeof(double));
  47.         public static readonly MethodInfo NormalizeSpace = GetMethod(typeof(XsltFunctions), "NormalizeSpace");
  48.         public static readonly MethodInfo Translate = GetMethod(typeof(XsltFunctions), "Translate");
  49.         public static readonly MethodInfo Lang = GetMethod(typeof(XsltFunctions), "Lang");
  50.         public static readonly MethodInfo Floor = GetMethod(typeof(Math), "Floor", typeof(double));
  51.         public static readonly MethodInfo Ceiling = GetMethod(typeof(Math), "Ceiling", typeof(double));
  52.         public static readonly MethodInfo Round = GetMethod(typeof(XsltFunctions), "Round");
  53.        
  54.         // XSLT functions and helper methods (static)
  55.         public static readonly MethodInfo SystemProperty = GetMethod(typeof(XsltFunctions), "SystemProperty");
  56.         public static readonly MethodInfo BaseUri = GetMethod(typeof(XsltFunctions), "BaseUri");
  57.         public static readonly MethodInfo OuterXml = GetMethod(typeof(XsltFunctions), "OuterXml");
  58.        
  59.         // MSXML extension functions
  60.         public static readonly MethodInfo MSFormatDateTime = GetMethod(typeof(XsltFunctions), "MSFormatDateTime");
  61.         public static readonly MethodInfo MSStringCompare = GetMethod(typeof(XsltFunctions), "MSStringCompare");
  62.         public static readonly MethodInfo MSUtc = GetMethod(typeof(XsltFunctions), "MSUtc");
  63.         public static readonly MethodInfo MSNumber = GetMethod(typeof(XsltFunctions), "MSNumber");
  64.         public static readonly MethodInfo MSLocalName = GetMethod(typeof(XsltFunctions), "MSLocalName");
  65.         public static readonly MethodInfo MSNamespaceUri = GetMethod(typeof(XsltFunctions), "MSNamespaceUri");
  66.        
  67.         // EXSLT functions
  68.         public static readonly MethodInfo EXslObjectType = GetMethod(typeof(XsltFunctions), "EXslObjectType");
  69.        
  70.         // XSLT functions and helper methods (non-static)
  71.         public static readonly MethodInfo CheckScriptNamespace = GetMethod(typeof(XsltLibrary), "CheckScriptNamespace");
  72.         public static readonly MethodInfo FunctionAvailable = GetMethod(typeof(XsltLibrary), "FunctionAvailable");
  73.         public static readonly MethodInfo ElementAvailable = GetMethod(typeof(XsltLibrary), "ElementAvailable");
  74.         public static readonly MethodInfo RegisterDecimalFormat = GetMethod(typeof(XsltLibrary), "RegisterDecimalFormat");
  75.         public static readonly MethodInfo RegisterDecimalFormatter = GetMethod(typeof(XsltLibrary), "RegisterDecimalFormatter");
  76.         public static readonly MethodInfo FormatNumberStatic = GetMethod(typeof(XsltLibrary), "FormatNumberStatic");
  77.         public static readonly MethodInfo FormatNumberDynamic = GetMethod(typeof(XsltLibrary), "FormatNumberDynamic");
  78.         public static readonly MethodInfo IsSameNodeSort = GetMethod(typeof(XsltLibrary), "IsSameNodeSort");
  79.         public static readonly MethodInfo LangToLcid = GetMethod(typeof(XsltLibrary), "LangToLcid");
  80.         public static readonly MethodInfo NumberFormat = GetMethod(typeof(XsltLibrary), "NumberFormat");
  81.        
  82.         public static MethodInfo GetMethod(Type className, string methName)
  83.         {
  84.             MethodInfo methInfo = className.GetMethod(methName);
  85.             Debug.Assert(methInfo != null, "Method " + className.Name + "." + methName + " not found");
  86.             return methInfo;
  87.         }
  88.        
  89.         public static MethodInfo GetMethod(Type className, string methName, params Type[] args)
  90.         {
  91.             MethodInfo methInfo = className.GetMethod(methName, args);
  92.             Debug.Assert(methInfo != null, "Method " + className.Name + "." + methName + " not found");
  93.             return methInfo;
  94.         }
  95.     }
  96.    
  97.     [EditorBrowsable(EditorBrowsableState.Never)]
  98.     public sealed class XsltLibrary
  99.     {
  100.         private XmlQueryRuntime runtime;
  101.         private HybridDictionary functionsAvail;
  102.         private DecimalFormats decimalFormats;
  103.         private List<DecimalFormatter> decimalFormatters;
  104.        
  105.         internal XsltLibrary(XmlQueryRuntime runtime)
  106.         {
  107.             this.runtime = runtime;
  108.         }
  109.        
  110.         public string FormatMessage(string res, IList<string> args)
  111.         {
  112.             string[] arr = new string[args.Count];
  113.            
  114.             for (int i = 0; i < arr.Length; i++)
  115.                 arr[i] = args[i];
  116.            
  117.             return XslTransformException.CreateMessage(res, arr);
  118.         }
  119.        
  120.         public int CheckScriptNamespace(string nsUri)
  121.         {
  122.             // Check that extension and script namespaces do not clash
  123.             if (runtime.ExternalContext.GetLateBoundObject(nsUri) != null) {
  124.                 throw new XslTransformException(Res.Xslt_ScriptAndExtensionClash, nsUri);
  125.             }
  126.             return 0;
  127.             // have to return something
  128.         }
  129.        
  130.         // Spec: http://www.w3.org/TR/xslt#function-element-available
  131.         public bool ElementAvailable(XmlQualifiedName name)
  132.         {
  133.             return QilGenerator.IsElementAvailable(name);
  134.         }
  135.        
  136.         // Spec: http://www.w3.org/TR/xslt#function-function-available
  137.         public bool FunctionAvailable(XmlQualifiedName name)
  138.         {
  139.             if (functionsAvail == null) {
  140.                 functionsAvail = new HybridDictionary();
  141.             }
  142.             else {
  143.                 object obj = functionsAvail[name];
  144.                 if (obj != null) {
  145.                     return (bool)obj;
  146.                 }
  147.             }
  148.            
  149.             bool result = FunctionAvailableHelper(name);
  150.             functionsAvail[name] = result;
  151.             return result;
  152.         }
  153.        
  154.         private bool FunctionAvailableHelper(XmlQualifiedName name)
  155.         {
  156.             // Is this an XPath or an XSLT function?
  157.             if (QilGenerator.IsFunctionAvailable(name.Name, name.Namespace)) {
  158.                 return true;
  159.             }
  160.            
  161.             // Script blocks and extension objects cannot implement neither null nor XSLT namespace
  162.             if (name.Namespace.Length == 0 || name.Namespace == XmlReservedNs.NsXslt) {
  163.                 return false;
  164.             }
  165.            
  166.             // Is this an extension object function?
  167.             if (runtime.ExternalContext.LateBoundFunctionExists(name.Name, name.Namespace)) {
  168.                 return true;
  169.             }
  170.            
  171.             // Is this a script function?
  172.             return runtime.EarlyBoundFunctionExists(name.Name, name.Namespace);
  173.         }
  174.        
  175.         public int RegisterDecimalFormat(XmlQualifiedName name, string infinitySymbol, string nanSymbol, string characters)
  176.         {
  177.             if (decimalFormats == null) {
  178.                 decimalFormats = new DecimalFormats();
  179.             }
  180.             DecimalFormatDecl format = new DecimalFormatDecl(name, infinitySymbol, nanSymbol, characters);
  181.             decimalFormats.Add(format);
  182.             return 0;
  183.             // have to return something
  184.         }
  185.        
  186.         private DecimalFormatter CreateDecimalFormatter(string formatPicture, string infinitySymbol, string nanSymbol, string characters)
  187.         {
  188.             NumberFormatInfo info = new NumberFormatInfo();
  189.             info.NumberDecimalSeparator = char.ToString(characters[0]);
  190.             info.NumberGroupSeparator = char.ToString(characters[1]);
  191.             info.PositiveInfinitySymbol = infinitySymbol;
  192.             info.NegativeSign = char.ToString(characters[7]);
  193.             info.NaNSymbol = nanSymbol;
  194.             info.PercentSymbol = char.ToString(characters[2]);
  195.             info.PerMilleSymbol = char.ToString(characters[3]);
  196.             info.NegativeInfinitySymbol = info.NegativeSign + info.PositiveInfinitySymbol;
  197.            
  198.             DecimalFormat formatInfo = new DecimalFormat(info, characters[5], characters[4], characters[6]);
  199.             return new DecimalFormatter(formatPicture, formatInfo);
  200.         }
  201.        
  202.         public double RegisterDecimalFormatter(string formatPicture, string infinitySymbol, string nanSymbol, string characters)
  203.         {
  204.             if (decimalFormatters == null) {
  205.                 decimalFormatters = new List<DecimalFormatter>();
  206.             }
  207.             decimalFormatters.Add(CreateDecimalFormatter(formatPicture, infinitySymbol, nanSymbol, characters));
  208.             return decimalFormatters.Count - 1;
  209.            
  210.         }
  211.        
  212.         public string FormatNumberStatic(double value, double decimalFormatterIndex)
  213.         {
  214.             int idx = (int)decimalFormatterIndex;
  215.             Debug.Assert(0 <= idx && idx < decimalFormatters.Count, "Value of decimalFormatterIndex is out of range");
  216.             return decimalFormatters[idx].Format(value);
  217.         }
  218.        
  219.         public string FormatNumberDynamic(double value, string formatPicture, XmlQualifiedName decimalFormatName, string errorMessageName)
  220.         {
  221.             DecimalFormatDecl format;
  222.             if (decimalFormats != null && decimalFormats.Contains(decimalFormatName)) {
  223.                 format = decimalFormats[decimalFormatName];
  224.             }
  225.             else {
  226.                 if (decimalFormatName != DecimalFormatDecl.Default.Name) {
  227.                     throw new XslTransformException(Res.Xslt_NoDecimalFormat, errorMessageName);
  228.                 }
  229.                 format = DecimalFormatDecl.Default;
  230.             }
  231.            
  232.             DecimalFormatter formatter = CreateDecimalFormatter(formatPicture, format.InfinitySymbol, format.NanSymbol, new string(format.Characters));
  233.             return formatter.Format(value);
  234.         }
  235.        
  236.         public string NumberFormat(IList<XPathItem> value, string formatString, double lang, string letterValue, string groupingSeparator, double groupingSize)
  237.         {
  238.             NumberFormatter formatter = new NumberFormatter(formatString, (int)lang, letterValue, groupingSeparator, (int)groupingSize);
  239.             return formatter.FormatSequence(value);
  240.         }
  241.        
  242.         internal const int InvariantCultureLcid = 127;
  243.        
  244.         public int LangToLcid(string lang, bool forwardCompatibility)
  245.         {
  246.             return LangToLcidInternal(lang, forwardCompatibility, null);
  247.         }
  248.        
  249.         static internal int LangToLcidInternal(string lang, bool forwardCompatibility, IErrorHelper errorHelper)
  250.         {
  251.             int lcid = InvariantCultureLcid;
  252.            
  253.             if (lang != null) {
  254.                 // Check if lang is valid language tag according to RFC 3066
  255.                 if (!XmlComplianceUtil.IsValidLanguageID(lang.ToCharArray(), 0, lang.Length)) {
  256.                     if (!forwardCompatibility) {
  257.                         if (errorHelper != null) {
  258.                                 /*[XT_032]*/                            errorHelper.ReportError(Res.Xslt_InvalidLanguageTag, lang);
  259.                         }
  260.                         else {
  261.                             throw new XslTransformException(Res.Xslt_InvalidLanguageTag, lang);
  262.                         }
  263.                     }
  264.                 }
  265.                 else {
  266.                     // Check if lang is a supported culture name
  267.                     try {
  268.                         lcid = new CultureInfo(lang).LCID;
  269.                     }
  270.                     catch (System.ArgumentException) {
  271.                         if (!forwardCompatibility) {
  272.                             if (errorHelper != null) {
  273.                                     /*[XT_033]*/                                errorHelper.ReportError(Res.Xslt_InvalidLanguage, lang);
  274.                             }
  275.                             else {
  276.                                 throw new XslTransformException(Res.Xslt_InvalidLanguage, lang);
  277.                             }
  278.                         }
  279.                     }
  280.                 }
  281.             }
  282.             return lcid;
  283.         }
  284.        
  285.         #region Comparisons
  286.         internal enum ComparisonOperator
  287.         {
  288. /*Equality  */            Eq,
  289.             Ne,
  290. /*Relational*/            Lt,
  291.             Le,
  292.             Gt,
  293.             Ge
  294.         }
  295.        
  296.         // Returns TypeCode of the given atomic value
  297.         private static TypeCode GetTypeCode(XPathItem item)
  298.         {
  299.             // Faster implementation of Type.GetTypeCode(item.ValueType);
  300.             Debug.Assert(!item.IsNode, "Atomic value expected");
  301.             Type itemType = item.ValueType;
  302.             if (itemType == XsltConvert.StringType) {
  303.                 return TypeCode.String;
  304.             }
  305.             else if (itemType == XsltConvert.DoubleType) {
  306.                 return TypeCode.Double;
  307.             }
  308.             else {
  309.                 Debug.Assert(itemType == XsltConvert.BooleanType, "Unexpected type of atomic value " + itemType.ToString());
  310.                 return TypeCode.Boolean;
  311.             }
  312.         }
  313.        
  314.         // Returns weakest of the two given TypeCodes, String > Double > Boolean
  315.         private static TypeCode WeakestTypeCode(TypeCode typeCode1, TypeCode typeCode2)
  316.         {
  317.             Debug.Assert(TypeCode.Boolean < TypeCode.Double && TypeCode.Double < TypeCode.String, "Cannot use the smallest TypeCode as a weakest one");
  318.             return typeCode1 < typeCode2 ? typeCode1 : typeCode2;
  319.         }
  320.        
  321.         private static bool CompareNumbers(ComparisonOperator op, double left, double right)
  322.         {
  323.             switch (op) {
  324.                 case ComparisonOperator.Eq:
  325.                     return left == right;
  326.                 case ComparisonOperator.Ne:
  327.                     return left != right;
  328.                 case ComparisonOperator.Lt:
  329.                     return left < right;
  330.                 case ComparisonOperator.Le:
  331.                     return left <= right;
  332.                 case ComparisonOperator.Gt:
  333.                     return left > right;
  334.                 default:
  335.                     return left >= right;
  336.             }
  337.         }
  338.        
  339.         private static bool CompareValues(ComparisonOperator op, XPathItem left, XPathItem right, TypeCode compType)
  340.         {
  341.             if (compType == TypeCode.Double) {
  342.                 return CompareNumbers(op, XsltConvert.ToDouble(left), XsltConvert.ToDouble(right));
  343.             }
  344.             else {
  345.                 Debug.Assert(op == ComparisonOperator.Eq || op == ComparisonOperator.Ne);
  346.                 if (compType == TypeCode.String) {
  347.                     return (XsltConvert.ToString(left) == XsltConvert.ToString(right)) == (op == ComparisonOperator.Eq);
  348.                 }
  349.                 else {
  350.                     Debug.Assert(compType == TypeCode.Boolean);
  351.                     return (XsltConvert.ToBoolean(left) == XsltConvert.ToBoolean(right)) == (op == ComparisonOperator.Eq);
  352.                 }
  353.             }
  354.         }
  355.        
  356.         private static bool CompareNodeSetAndValue(ComparisonOperator op, IList<XPathNavigator> nodeset, XPathItem val, TypeCode compType)
  357.         {
  358.             Debug.Assert(compType == TypeCode.Boolean || compType == TypeCode.Double || compType == TypeCode.String);
  359.             if (compType == TypeCode.Boolean) {
  360.                 // Cast nodeset to boolean type, then take its ordinal number
  361.                 return CompareNumbers(op, (nodeset.Count != 0) ? 1 : 0, XsltConvert.ToBoolean(val) ? 1 : 0);
  362.             }
  363.             else {
  364.                 int length = nodeset.Count;
  365.                 for (int idx = 0; idx < length; idx++) {
  366.                     if (CompareValues(op, nodeset[idx], val, compType)) {
  367.                         return true;
  368.                     }
  369.                 }
  370.                 return false;
  371.             }
  372.         }
  373.        
  374.         private static bool CompareNodeSetAndNodeSet(ComparisonOperator op, IList<XPathNavigator> left, IList<XPathNavigator> right, TypeCode compType)
  375.         {
  376.             int leftLen = left.Count;
  377.             int rightLen = right.Count;
  378.             for (int leftIdx = 0; leftIdx < leftLen; leftIdx++) {
  379.                 for (int rightIdx = 0; rightIdx < rightLen; rightIdx++) {
  380.                     if (CompareValues(op, left[leftIdx], right[rightIdx], compType)) {
  381.                         return true;
  382.                     }
  383.                 }
  384.             }
  385.             return false;
  386.         }
  387.        
  388.         public bool EqualityOperator(double opCode, IList<XPathItem> left, IList<XPathItem> right)
  389.         {
  390.             ComparisonOperator op = (ComparisonOperator)opCode;
  391.             Debug.Assert(op == ComparisonOperator.Eq || op == ComparisonOperator.Ne);
  392.             CheckXsltValue(left);
  393.             CheckXsltValue(right);
  394.            
  395.             if (IsNodeSetOrRtf(left)) {
  396.                 if (IsNodeSetOrRtf(right)) {
  397.                     // Both left and right are node-sets
  398.                     return CompareNodeSetAndNodeSet(op, ToNodeSetOrRtf(left), ToNodeSetOrRtf(right), TypeCode.String);
  399.                 }
  400.                 else {
  401.                     // left is a node-set, right is an atomic value
  402.                     XPathItem rightItem = right[0];
  403.                     return CompareNodeSetAndValue(op, ToNodeSetOrRtf(left), rightItem, GetTypeCode(rightItem));
  404.                 }
  405.             }
  406.             else if (IsNodeSetOrRtf(right)) {
  407.                 // left is an atomic value, right is a node-set
  408.                 XPathItem leftItem = left[0];
  409.                 // Swap operands: left op right -> right op left
  410.                 return CompareNodeSetAndValue(op, ToNodeSetOrRtf(right), leftItem, GetTypeCode(leftItem));
  411.             }
  412.             else {
  413.                 // Both left and right are atomic values
  414.                 XPathItem leftItem = left[0];
  415.                 XPathItem rightItem = right[0];
  416.                 return CompareValues(op, leftItem, rightItem, WeakestTypeCode(GetTypeCode(leftItem), GetTypeCode(rightItem)));
  417.             }
  418.         }
  419.        
  420.         // Inverts relational operator in order to swap operands of the comparison
  421.         private static ComparisonOperator InvertOperator(ComparisonOperator op)
  422.         {
  423.             switch (op) {
  424.                 case ComparisonOperator.Lt:
  425.                     return ComparisonOperator.Gt;
  426.                 case ComparisonOperator.Le:
  427.                     return ComparisonOperator.Ge;
  428.                 case ComparisonOperator.Gt:
  429.                     return ComparisonOperator.Lt;
  430.                 case ComparisonOperator.Ge:
  431.                     return ComparisonOperator.Le;
  432.                 default:
  433.                     return op;
  434.             }
  435.         }
  436.        
  437.         public bool RelationalOperator(double opCode, IList<XPathItem> left, IList<XPathItem> right)
  438.         {
  439.             ComparisonOperator op = (ComparisonOperator)opCode;
  440.             Debug.Assert(ComparisonOperator.Lt <= op && op <= ComparisonOperator.Ge);
  441.             CheckXsltValue(left);
  442.             CheckXsltValue(right);
  443.            
  444.             if (IsNodeSetOrRtf(left)) {
  445.                 if (IsNodeSetOrRtf(right)) {
  446.                     // Both left and right are node-sets
  447.                     return CompareNodeSetAndNodeSet(op, ToNodeSetOrRtf(left), ToNodeSetOrRtf(right), TypeCode.Double);
  448.                 }
  449.                 else {
  450.                     // left is a node-set, right is an atomic value
  451.                     XPathItem rightItem = right[0];
  452.                     return CompareNodeSetAndValue(op, ToNodeSetOrRtf(left), rightItem, WeakestTypeCode(GetTypeCode(rightItem), TypeCode.Double));
  453.                 }
  454.             }
  455.             else if (IsNodeSetOrRtf(right)) {
  456.                 // left is an atomic value, right is a node-set
  457.                 XPathItem leftItem = left[0];
  458.                 // Swap operands: left op right -> right InvertOperator(op) left
  459.                 op = InvertOperator(op);
  460.                 return CompareNodeSetAndValue(op, ToNodeSetOrRtf(right), leftItem, WeakestTypeCode(GetTypeCode(leftItem), TypeCode.Double));
  461.             }
  462.             else {
  463.                 // Both left and right are atomic values
  464.                 XPathItem leftItem = left[0];
  465.                 XPathItem rightItem = right[0];
  466.                 return CompareValues(op, leftItem, rightItem, TypeCode.Double);
  467.             }
  468.         }
  469.         #endregion
  470.        
  471.         // nav1 and nav2 are assumed to belong to the same document
  472.         public bool IsSameNodeSort(XPathNavigator nav1, XPathNavigator nav2)
  473.         {
  474.             Debug.Assert(XPathNodeType.SignificantWhitespace == XPathNodeType.Text + 1);
  475.             Debug.Assert(XPathNodeType.Whitespace == XPathNodeType.Text + 2);
  476.            
  477.             XPathNodeType nt1 = nav1.NodeType;
  478.             XPathNodeType nt2 = nav2.NodeType;
  479.            
  480.             // If one of nodes is a text node, the other one must also be a text node
  481.             if (XPathNodeType.Text <= nt1 && nt1 <= XPathNodeType.Whitespace) {
  482.                 return XPathNodeType.Text <= nt2 && nt2 <= XPathNodeType.Whitespace;
  483.             }
  484.            
  485.             // Otherwise nodes must have the same node kind, the same local name, and the same namespace URI
  486.             Debug.Assert((object)nav1.NameTable == (object)nav2.NameTable, "Ref.Equal cannot be used if navigators have different name tables");
  487.             return nt1 == nt2 && Ref.Equal(nav1.LocalName, nav2.LocalName) && Ref.Equal(nav1.NamespaceURI, nav2.NamespaceURI);
  488.         }
  489.        
  490.        
  491.         //------------------------------------------------
  492.         // Helper methods
  493.         //------------------------------------------------
  494.        
  495.         [Conditional("DEBUG")]
  496.         static internal void CheckXsltValue(XPathItem item)
  497.         {
  498.             CheckXsltValue(new XmlQueryItemSequence(item));
  499.         }
  500.        
  501.         [Conditional("DEBUG")]
  502.         static internal void CheckXsltValue(IList<XPathItem> val)
  503.         {
  504.             // IsDocOrderDistinct is not always set to true even if the node-set is ordered
  505.             // Debug.Assert(val.Count <= 1 || val.IsDocOrderDistinct, "All node-sets must be ordered");
  506.            
  507.             if (val.Count == 1) {
  508.                 XsltFunctions.EXslObjectType(val);
  509.             }
  510.             else {
  511.                 // Every item must be a node, but for performance reasons we check only
  512.                 // the first two and the last two items
  513.                 int count = val.Count;
  514.                 for (int idx = 0; idx < count; idx++) {
  515.                     if (!val[idx].IsNode) {
  516.                         Debug.Fail("Invalid XSLT value");
  517.                         break;
  518.                     }
  519.                     if (idx == 1) {
  520.                         idx += Math.Max(count - 4, 0);
  521.                     }
  522.                 }
  523.             }
  524.         }
  525.        
  526.         private static bool IsNodeSetOrRtf(IList<XPathItem> val)
  527.         {
  528.             CheckXsltValue(val);
  529.             if (val.Count == 1) {
  530.                 return val[0].IsNode;
  531.             }
  532.             return true;
  533.         }
  534.        
  535.         private static IList<XPathNavigator> ToNodeSetOrRtf(IList<XPathItem> val)
  536.         {
  537.             return XmlILStorageConverter.ItemsToNavigators(val);
  538.         }
  539.     }
  540. }

Developer Fusion