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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XsltFunctions.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.IO;
  16. using System.Text;
  17. using System.Reflection;
  18. using System.Diagnostics;
  19. using System.ComponentModel;
  20. using System.Globalization;
  21. using System.Collections;
  22. using System.Collections.Generic;
  23. using System.Collections.Specialized;
  24. using System.Xml.Schema;
  25. using System.Xml.XPath;
  26. using System.Xml.Xsl.Xslt;
  27. using System.Runtime.InteropServices;
  28. namespace System.Xml.Xsl.Runtime
  29. {
  30.     using Res = System.Xml.Utils.Res;
  31.    
  32.     [EditorBrowsable(EditorBrowsableState.Never)]
  33.     public static class XsltFunctions
  34.     {
  35.         private static readonly CompareInfo compareInfo = CultureInfo.InvariantCulture.CompareInfo;
  36.        
  37.        
  38.         //------------------------------------------------
  39.         // Xslt/XPath functions
  40.         //------------------------------------------------
  41.        
  42.         public static bool StartsWith(string s1, string s2)
  43.         {
  44.             //return collation.IsPrefix(s1, s2);
  45.             return s1.Length >= s2.Length && string.CompareOrdinal(s1, 0, s2, 0, s2.Length) == 0;
  46.         }
  47.        
  48.         public static bool Contains(string s1, string s2)
  49.         {
  50.             //return collation.IndexOf(s1, s2) >= 0;
  51.             return compareInfo.IndexOf(s1, s2, CompareOptions.Ordinal) >= 0;
  52.         }
  53.        
  54.         public static string SubstringBefore(string s1, string s2)
  55.         {
  56.             if (s2.Length == 0) {
  57.                 return s2;
  58.             }
  59.             //int idx = collation.IndexOf(s1, s2);
  60.             int idx = compareInfo.IndexOf(s1, s2, CompareOptions.Ordinal);
  61.             return (idx < 1) ? string.Empty : s1.Substring(0, idx);
  62.         }
  63.        
  64.         public static string SubstringAfter(string s1, string s2)
  65.         {
  66.             if (s2.Length == 0) {
  67.                 return s1;
  68.             }
  69.             //int idx = collation.IndexOf(s1, s2);
  70.             int idx = compareInfo.IndexOf(s1, s2, CompareOptions.Ordinal);
  71.             return (idx < 0) ? string.Empty : s1.Substring(idx + s2.Length);
  72.         }
  73.        
  74.         public static string Substring(string value, double startIndex)
  75.         {
  76.             startIndex = Round(startIndex);
  77.             if (startIndex <= 0) {
  78.                 return value;
  79.             }
  80.             else if (startIndex <= value.Length) {
  81.                 return value.Substring((int)startIndex - 1);
  82.             }
  83.             else {
  84.                 Debug.Assert(value.Length < startIndex || Double.IsNaN(startIndex));
  85.                 return string.Empty;
  86.             }
  87.         }
  88.        
  89.         public static string Substring(string value, double startIndex, double length)
  90.         {
  91.             startIndex = Round(startIndex) - 1;
  92.             // start index
  93.             if (startIndex >= value.Length) {
  94.                 return string.Empty;
  95.             }
  96.            
  97.             double endIndex = startIndex + Round(length);
  98.             // end index
  99.             startIndex = (startIndex <= 0) ? 0 : startIndex;
  100.            
  101.             if (startIndex < endIndex) {
  102.                 if (endIndex > value.Length) {
  103.                     endIndex = value.Length;
  104.                 }
  105.                 Debug.Assert(0 <= startIndex && startIndex <= endIndex && endIndex <= value.Length);
  106.                 return value.Substring((int)startIndex, (int)(endIndex - startIndex));
  107.             }
  108.             else {
  109.                 Debug.Assert(endIndex <= startIndex || Double.IsNaN(endIndex));
  110.                 return string.Empty;
  111.             }
  112.         }
  113.        
  114.         public static string NormalizeSpace(string value)
  115.         {
  116.             XmlCharType xmlCharType = XmlCharType.Instance;
  117.             StringBuilder sb = null;
  118.             int idx;
  119.             int idxStart = 0;
  120.             int idxSpace = 0;
  121.            
  122.             for (idx = 0; idx < value.Length; idx++) {
  123.                 if (xmlCharType.IsWhiteSpace(value[idx])) {
  124.                     if (idx == idxStart) {
  125.                         // Previous character was a whitespace character, so discard this character
  126.                         idxStart++;
  127.                     }
  128.                     else if (value[idx] != ' ' || idxSpace == idx) {
  129.                         // Space was previous character or this is a non-space character
  130.                         if (sb == null)
  131.                             sb = new StringBuilder(value.Length);
  132.                         else
  133.                             sb.Append(' ');
  134.                        
  135.                         // Copy non-space characters into string builder
  136.                         if (idxSpace == idx)
  137.                             sb.Append(value, idxStart, idx - idxStart - 1);
  138.                         else
  139.                             sb.Append(value, idxStart, idx - idxStart);
  140.                        
  141.                         idxStart = idx + 1;
  142.                     }
  143.                     else {
  144.                         // Single whitespace character doesn't cause normalization, but mark its position
  145.                         idxSpace = idx + 1;
  146.                     }
  147.                 }
  148.             }
  149.            
  150.             if (sb == null) {
  151.                 // Check for string that is entirely composed of whitespace
  152.                 if (idxStart == idx)
  153.                     return string.Empty;
  154.                
  155.                 // If string does not end with a space, then it must already be normalized
  156.                 if (idxStart == 0 && idxSpace != idx)
  157.                     return value;
  158.                
  159.                 sb = new StringBuilder(value.Length);
  160.             }
  161.             else if (idx != idxStart) {
  162.                 sb.Append(' ');
  163.             }
  164.            
  165.             // Copy non-space characters into string builder
  166.             if (idxSpace == idx)
  167.                 sb.Append(value, idxStart, idx - idxStart - 1);
  168.             else
  169.                 sb.Append(value, idxStart, idx - idxStart);
  170.            
  171.             return sb.ToString();
  172.         }
  173.        
  174.         public static string Translate(string arg, string mapString, string transString)
  175.         {
  176.             if (mapString.Length == 0) {
  177.                 return arg;
  178.             }
  179.            
  180.             StringBuilder sb = new StringBuilder(arg.Length);
  181.            
  182.             for (int i = 0; i < arg.Length; i++) {
  183.                 int index = mapString.IndexOf(arg[i]);
  184.                 if (index < 0) {
  185.                     // Keep the character
  186.                     sb.Append(arg[i]);
  187.                 }
  188.                 else if (index < transString.Length) {
  189.                     // Replace the character
  190.                     sb.Append(transString[index]);
  191.                 }
  192.                 else {
  193.                     // Remove the character
  194.                 }
  195.             }
  196.             return sb.ToString();
  197.         }
  198.        
  199.         public static bool Lang(string value, XPathNavigator context)
  200.         {
  201.             string lang = context.XmlLang;
  202.            
  203.             if (!lang.StartsWith(value, StringComparison.OrdinalIgnoreCase)) {
  204.                 return false;
  205.             }
  206.             return (lang.Length == value.Length || lang[value.Length] == '-');
  207.         }
  208.        
  209.         // Round value using XPath rounding rules (round towards positive infinity).
  210.         // Values between -0.5 and -0.0 are rounded to -0.0 (negative zero).
  211.         public static double Round(double value)
  212.         {
  213.             double temp = Math.Round(value);
  214.             return (value - temp == 0.5) ? temp + 1 : temp;
  215.         }
  216.        
  217.         // Spec: http://www.w3.org/TR/xslt.html#function-system-property
  218.         public static XPathItem SystemProperty(XmlQualifiedName name)
  219.         {
  220.             if (name.Namespace == XmlReservedNs.NsXslt) {
  221.                 // "xsl:version" must return 1.0 as a number, see http://www.w3.org/TR/xslt20/#incompatility-without-schema
  222.                 switch (name.Name) {
  223.                     case "version":
  224.                         return new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Double), 1.0);
  225.                     case "vendor":
  226.                         return new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String), "Microsoft");
  227.                     case "vendor-url":
  228.                         return new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String), "http://www.microsoft.com");
  229.                 }
  230.             }
  231.             else if (name.Namespace == XmlReservedNs.NsMsxsl && name.Name == "version") {
  232.                 // msxsl:version
  233.                 return new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String), typeof(XsltLibrary).Assembly.ImageRuntimeVersion);
  234.             }
  235.             // If the property name is not recognized, return the empty string
  236.             return new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String), string.Empty);
  237.         }
  238.        
  239.        
  240.         //------------------------------------------------
  241.         // Navigator functions
  242.         //------------------------------------------------
  243.        
  244.         public static string BaseUri(XPathNavigator navigator)
  245.         {
  246.             return navigator.BaseURI;
  247.         }
  248.        
  249.         public static string OuterXml(XPathNavigator navigator)
  250.         {
  251.             RtfNavigator rtf = navigator as RtfNavigator;
  252.             if (rtf == null) {
  253.                 return navigator.OuterXml;
  254.             }
  255.             StringBuilder sb = new StringBuilder();
  256.             XmlWriterSettings settings = new XmlWriterSettings();
  257.             settings.OmitXmlDeclaration = true;
  258.             settings.ConformanceLevel = ConformanceLevel.Fragment;
  259.             settings.CheckCharacters = false;
  260.             XmlWriter xw = XmlWriter.Create(sb, settings);
  261.             rtf.CopyToWriter(xw);
  262.             xw.Close();
  263.             return sb.ToString();
  264.         }
  265.        
  266.        
  267.         //------------------------------------------------
  268.         // EXslt Functions
  269.         //------------------------------------------------
  270.        
  271.         public static string EXslObjectType(IList<XPathItem> value)
  272.         {
  273.             if (value.Count != 1) {
  274.                 XsltLibrary.CheckXsltValue(value);
  275.                 return "node-set";
  276.             }
  277.            
  278.             XPathItem item = value[0];
  279.             if (item is RtfNavigator) {
  280.                 return "RTF";
  281.             }
  282.             else if (item.IsNode) {
  283.                 Debug.Assert(item is XPathNavigator);
  284.                 return "node-set";
  285.             }
  286.            
  287.             object o = item.TypedValue;
  288.             if (o is string) {
  289.                 return "string";
  290.             }
  291.             else if (o is double) {
  292.                 return "number";
  293.             }
  294.             else if (o is bool) {
  295.                 return "boolean";
  296.             }
  297.             else {
  298.                 Debug.Fail("Unexpected type: " + o.GetType().ToString());
  299.                 return "external";
  300.             }
  301.         }
  302.        
  303.        
  304.         //------------------------------------------------
  305.         // Msxml Extension Functions
  306.         //------------------------------------------------
  307.        
  308.         public static double MSNumber(IList<XPathItem> value)
  309.         {
  310.             XsltLibrary.CheckXsltValue(value);
  311.             if (value.Count == 0) {
  312.                 return Double.NaN;
  313.             }
  314.             XPathItem item = value[0];
  315.            
  316.             string stringValue;
  317.            
  318.             if (item.IsNode) {
  319.                 stringValue = item.Value;
  320.             }
  321.             else {
  322.                 Type itemType = item.ValueType;
  323.                 if (itemType == XsltConvert.StringType) {
  324.                     stringValue = item.Value;
  325.                 }
  326.                 else if (itemType == XsltConvert.DoubleType) {
  327.                     return item.ValueAsDouble;
  328.                 }
  329.                 else {
  330.                     Debug.Assert(itemType == XsltConvert.BooleanType, "Unexpected type of atomic value " + itemType.ToString());
  331.                     return item.ValueAsBoolean ? 1.0 : 0.0;
  332.                 }
  333.             }
  334.            
  335.             Debug.Assert(stringValue != null);
  336.             double d;
  337.             if (XmlConvert.TryToDouble(stringValue, out d) != null) {
  338.                 d = double.NaN;
  339.             }
  340.             return d;
  341.         }
  342.        
  343.         // CharSet.Auto is needed to work on Windows 98 and Windows Me
  344.         [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
  345.         static extern int GetDateFormat(int locale, uint dwFlags, ref SystemTime sysTime, string format, StringBuilder sb, int sbSize);
  346.        
  347.         [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
  348.         static extern int GetTimeFormat(int locale, uint dwFlags, ref SystemTime sysTime, string format, StringBuilder sb, int sbSize);
  349.        
  350.         [StructLayout(LayoutKind.Sequential)]
  351.         private struct SystemTime
  352.         {
  353.             [MarshalAs(UnmanagedType.U2)]
  354.             public ushort Year;
  355.             [MarshalAs(UnmanagedType.U2)]
  356.             public ushort Month;
  357.             [MarshalAs(UnmanagedType.U2)]
  358.             public ushort DayOfWeek;
  359.             [MarshalAs(UnmanagedType.U2)]
  360.             public ushort Day;
  361.             [MarshalAs(UnmanagedType.U2)]
  362.             public ushort Hour;
  363.             [MarshalAs(UnmanagedType.U2)]
  364.             public ushort Minute;
  365.             [MarshalAs(UnmanagedType.U2)]
  366.             public ushort Second;
  367.             [MarshalAs(UnmanagedType.U2)]
  368.             public ushort Milliseconds;
  369.            
  370.             public SystemTime(DateTime dateTime)
  371.             {
  372.                 this.Year = (ushort)dateTime.Year;
  373.                 this.Month = (ushort)dateTime.Month;
  374.                 this.DayOfWeek = (ushort)dateTime.DayOfWeek;
  375.                 this.Day = (ushort)dateTime.Day;
  376.                 this.Hour = (ushort)dateTime.Hour;
  377.                 this.Minute = (ushort)dateTime.Minute;
  378.                 this.Second = (ushort)dateTime.Second;
  379.                 this.Milliseconds = (ushort)dateTime.Millisecond;
  380.             }
  381.         }
  382.        
  383.         // string ms:format-date(string datetime[, string format[, string language]])
  384.         // string ms:format-time(string datetime[, string format[, string language]])
  385.         //
  386.         // Format xsd:dateTime as a date/time string for a given language using a given format string.
  387.         // * Datetime contains a lexical representation of xsd:dateTime. If datetime is not valid, the
  388.         // empty string is returned.
  389.         // * Format specifies a format string in the same way as for GetDateFormat/GetTimeFormat system
  390.         // functions. If format is the empty string or not passed, the default date/time format for the
  391.         // given culture is used.
  392.         // * Language specifies a culture used for formatting. If language is the empty string or not
  393.         // passed, the current culture is used. If language is not recognized, a runtime error happens.
  394.         public static string MSFormatDateTime(string dateTime, string format, string lang, bool isDate)
  395.         {
  396.             try {
  397.                 int locale = GetCultureInfo(lang).LCID;
  398.                
  399.                 XsdDateTime xdt;
  400.                 if (!XsdDateTime.TryParse(dateTime, XsdDateTimeFlags.AllXsd | XsdDateTimeFlags.XdrDateTime | XsdDateTimeFlags.XdrTimeNoTz, out xdt)) {
  401.                     return string.Empty;
  402.                 }
  403.                 SystemTime st = new SystemTime(xdt.ToZulu());
  404.                
  405.                 StringBuilder sb = new StringBuilder(format.Length + 16);
  406.                
  407.                 // If format is the empty string or not specified, use the default format for the given locale
  408.                 if (format.Length == 0) {
  409.                     format = null;
  410.                 }
  411.                 if (isDate) {
  412.                     int res = GetDateFormat(locale, 0, ref st, format, sb, sb.Capacity);
  413.                     if (res == 0) {
  414.                         res = GetDateFormat(locale, 0, ref st, format, sb, 0);
  415.                         if (res != 0) {
  416.                             sb = new StringBuilder(res);
  417.                             res = GetDateFormat(locale, 0, ref st, format, sb, sb.Capacity);
  418.                         }
  419.                     }
  420.                 }
  421.                 else {
  422.                     int res = GetTimeFormat(locale, 0, ref st, format, sb, sb.Capacity);
  423.                     if (res == 0) {
  424.                         res = GetTimeFormat(locale, 0, ref st, format, sb, 0);
  425.                         if (res != 0) {
  426.                             sb = new StringBuilder(res);
  427.                             res = GetTimeFormat(locale, 0, ref st, format, sb, sb.Capacity);
  428.                         }
  429.                     }
  430.                 }
  431.                 return sb.ToString();
  432.             }
  433.             catch (ArgumentException) {
  434.                 // Operations with DateTime can throw this exception eventualy
  435.                 return string.Empty;
  436.             }
  437.         }
  438.        
  439.         public static double MSStringCompare(string s1, string s2, string lang, string options)
  440.         {
  441.             CultureInfo cultinfo = GetCultureInfo(lang);
  442.             CompareOptions opts = CompareOptions.None;
  443.             bool upperFirst = false;
  444.             for (int idx = 0; idx < options.Length; idx++) {
  445.                 switch (options[idx]) {
  446.                     case 'i':
  447.                         opts = CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth;
  448.                         break;
  449.                     case 'u':
  450.                         upperFirst = true;
  451.                         break;
  452.                     default:
  453.                         upperFirst = true;
  454.                         opts = CompareOptions.IgnoreCase;
  455.                         break;
  456.                 }
  457.             }
  458.            
  459.             if (upperFirst) {
  460.                 if (opts != CompareOptions.None) {
  461.                     throw new XslTransformException(Res.Xslt_InvalidCompareOption, options);
  462.                 }
  463.                 opts = CompareOptions.IgnoreCase;
  464.             }
  465.            
  466.             int result = cultinfo.CompareInfo.Compare(s1, s2, opts);
  467.             if (upperFirst && result == 0) {
  468.                 result = -cultinfo.CompareInfo.Compare(s1, s2, CompareOptions.None);
  469.             }
  470.             return result;
  471.         }
  472.        
  473.         public static string MSUtc(string dateTime)
  474.         {
  475.             XsdDateTime xdt;
  476.             DateTime dt;
  477.             try {
  478.                 if (!XsdDateTime.TryParse(dateTime, XsdDateTimeFlags.AllXsd | XsdDateTimeFlags.XdrDateTime | XsdDateTimeFlags.XdrTimeNoTz, out xdt)) {
  479.                     return string.Empty;
  480.                 }
  481.                 dt = xdt.ToZulu();
  482.             }
  483.             catch (ArgumentException) {
  484.                 // Operations with DateTime can throw this exception eventualy
  485.                 return string.Empty;
  486.             }
  487.             char[] text = "----------T00:00:00.000".ToCharArray();
  488.             // "YYYY-MM-DDTHH:NN:SS.III"
  489.             // 0 1 2
  490.             // 01234567890123456789012
  491.             switch (xdt.TypeCode) {
  492.                 case XmlTypeCode.DateTime:
  493.                     PrintDate(text, dt);
  494.                     PrintTime(text, dt);
  495.                     break;
  496.                 case XmlTypeCode.Time:
  497.                     PrintTime(text, dt);
  498.                     break;
  499.                 case XmlTypeCode.Date:
  500.                     PrintDate(text, dt);
  501.                     break;
  502.                 case XmlTypeCode.GYearMonth:
  503.                     PrintYear(text, dt.Year);
  504.                     ShortToCharArray(text, 5, dt.Month);
  505.                     break;
  506.                 case XmlTypeCode.GYear:
  507.                     PrintYear(text, dt.Year);
  508.                     break;
  509.                 case XmlTypeCode.GMonthDay:
  510.                     ShortToCharArray(text, 5, dt.Month);
  511.                     ShortToCharArray(text, 8, dt.Day);
  512.                     break;
  513.                 case XmlTypeCode.GDay:
  514.                     ShortToCharArray(text, 8, dt.Day);
  515.                     break;
  516.                 case XmlTypeCode.GMonth:
  517.                     ShortToCharArray(text, 5, dt.Month);
  518.                     break;
  519.             }
  520.             return new string(text);
  521.         }
  522.        
  523.         public static string MSLocalName(string name)
  524.         {
  525.             int colonOffset;
  526.             int len = ValidateNames.ParseQName(name, 0, out colonOffset);
  527.            
  528.             if (len != name.Length) {
  529.                 return string.Empty;
  530.             }
  531.             if (colonOffset == 0) {
  532.                 return name;
  533.             }
  534.             else {
  535.                 return name.Substring(colonOffset + 1);
  536.             }
  537.         }
  538.        
  539.         public static string MSNamespaceUri(string name, XPathNavigator currentNode)
  540.         {
  541.             int colonOffset;
  542.             int len = ValidateNames.ParseQName(name, 0, out colonOffset);
  543.            
  544.             if (len != name.Length) {
  545.                 return string.Empty;
  546.             }
  547.             string prefix = name.Substring(0, colonOffset);
  548.             if (prefix == "xmlns") {
  549.                 return string.Empty;
  550.             }
  551.             string ns = currentNode.LookupNamespace(prefix);
  552.             if (ns != null) {
  553.                 return ns;
  554.             }
  555.             if (prefix == "xml") {
  556.                 return XmlReservedNs.NsXml;
  557.             }
  558.             return string.Empty;
  559.         }
  560.        
  561.        
  562.         //------------------------------------------------
  563.         // Helper Functions
  564.         //------------------------------------------------
  565.        
  566.         private static CultureInfo GetCultureInfo(string lang)
  567.         {
  568.             Debug.Assert(lang != null);
  569.             if (lang.Length == 0) {
  570.                 return CultureInfo.CurrentCulture;
  571.             }
  572.             else {
  573.                 try {
  574.                     return new CultureInfo(lang);
  575.                 }
  576.                 catch (System.ArgumentException) {
  577.                     throw new XslTransformException(Res.Xslt_InvalidLanguage, lang);
  578.                 }
  579.             }
  580.         }
  581.        
  582.         private static void PrintDate(char[] text, DateTime dt)
  583.         {
  584.             PrintYear(text, dt.Year);
  585.             ShortToCharArray(text, 5, dt.Month);
  586.             ShortToCharArray(text, 8, dt.Day);
  587.         }
  588.        
  589.         private static void PrintTime(char[] text, DateTime dt)
  590.         {
  591.             ShortToCharArray(text, 11, dt.Hour);
  592.             ShortToCharArray(text, 14, dt.Minute);
  593.             ShortToCharArray(text, 17, dt.Second);
  594.             PrintMsec(text, dt.Millisecond);
  595.         }
  596.        
  597.         private static void PrintYear(char[] text, int value)
  598.         {
  599.             text[0] = (char)((value / 1000) % 10 + '0');
  600.             text[1] = (char)((value / 100) % 10 + '0');
  601.             text[2] = (char)((value / 10) % 10 + '0');
  602.             text[3] = (char)((value / 1) % 10 + '0');
  603.         }
  604.        
  605.         private static void PrintMsec(char[] text, int value)
  606.         {
  607.             if (value == 0) {
  608.                 return;
  609.             }
  610.             text[20] = (char)((value / 100) % 10 + '0');
  611.             text[21] = (char)((value / 10) % 10 + '0');
  612.             text[22] = (char)((value / 1) % 10 + '0');
  613.         }
  614.        
  615.         private static void ShortToCharArray(char[] text, int start, int value)
  616.         {
  617.             text[start] = (char)(value / 10 + '0');
  618.             text[start + 1] = (char)(value % 10 + '0');
  619.         }
  620.     }
  621. }

Developer Fusion