File: System\Xml\Xsl\Runtime\XsltConvert.cs
Web Access
Project: src\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Xml.Schema;
using System.Xml.XPath;
 
namespace System.Xml.Xsl.Runtime
{
    /// <summary>
    /// Contains conversion routines used by Xslt.  These conversions fall into several categories:
    ///   1. Internal type to internal type: These are conversions from one of the five Xslt types to another
    ///      of the five types.
    ///   2. External type to internal type: These are conversions from any of the Xsd types to one of the five
    ///      Xslt types.
    ///   3. Internal type to external type: These are conversions from one of the five Xslt types to any of
    ///      of the Xsd types.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public static class XsltConvert
    {
        //------------------------------------------------------------------------
        // ToBoolean (internal type to internal type)
        //------------------------------------------------------------------------
 
        public static bool ToBoolean(XPathItem item)
        {
            XsltLibrary.CheckXsltValue(item);
 
            if (item.IsNode)
                return true;
 
            Type itemType = item.ValueType;
 
            if (itemType == typeof(string))
            {
                return item.Value.Length != 0;
            }
            else if (itemType == typeof(double))
            {
                // (x < 0 || 0 < x)  ==  (x != 0) && !Double.IsNaN(x)
                double dbl = item.ValueAsDouble;
                return dbl < 0 || 0 < dbl;
            }
            else
            {
                Debug.Assert(itemType == typeof(bool), $"Unexpected type of atomic sequence {itemType}");
                return item.ValueAsBoolean;
            }
        }
 
        public static bool ToBoolean(IList<XPathItem> listItems)
        {
            XsltLibrary.CheckXsltValue(listItems);
 
            if (listItems.Count == 0)
                return false;
 
            return ToBoolean(listItems[0]);
        }
 
 
        //------------------------------------------------------------------------
        // ToDouble (internal type to internal type)
        //------------------------------------------------------------------------
 
        public static double ToDouble(string value)
        {
            return XPathConvert.StringToDouble(value);
        }
 
        public static double ToDouble(XPathItem item)
        {
            XsltLibrary.CheckXsltValue(item);
 
            if (item.IsNode)
                return XPathConvert.StringToDouble(item.Value);
 
            Type itemType = item.ValueType;
 
            if (itemType == typeof(string))
            {
                return XPathConvert.StringToDouble(item.Value);
            }
            else if (itemType == typeof(double))
            {
                return item.ValueAsDouble;
            }
            else
            {
                Debug.Assert(itemType == typeof(bool), $"Unexpected type of atomic sequence {itemType}");
                return item.ValueAsBoolean ? 1d : 0d;
            }
        }
 
        public static double ToDouble(IList<XPathItem> listItems)
        {
            XsltLibrary.CheckXsltValue(listItems);
 
            if (listItems.Count == 0)
                return double.NaN;
 
            return ToDouble(listItems[0]);
        }
 
 
        //------------------------------------------------------------------------
        // ToNode (internal type to internal type)
        //------------------------------------------------------------------------
 
        public static XPathNavigator ToNode(XPathItem item)
        {
            XsltLibrary.CheckXsltValue(item);
 
            if (!item.IsNode)
            {
                // Create Navigator over text node containing string value of item
                XPathDocument doc = new XPathDocument();
                XmlRawWriter writer = doc.LoadFromWriter(XPathDocument.LoadFlags.AtomizeNames, string.Empty);
                writer.WriteString(ToString(item));
                writer.Close();
                return doc.CreateNavigator();
            }
 
            RtfNavigator? rtf = item as RtfNavigator;
            if (rtf != null)
                return rtf.ToNavigator();
 
            return (XPathNavigator)item;
        }
 
        public static XPathNavigator ToNode(IList<XPathItem> listItems)
        {
            XsltLibrary.CheckXsltValue(listItems);
 
            if (listItems.Count == 1)
                return ToNode(listItems[0]);
 
            throw new XslTransformException(SR.Xslt_NodeSetNotNode, string.Empty);
        }
 
 
        //------------------------------------------------------------------------
        // ToNodes (internal type to internal type)
        //------------------------------------------------------------------------
 
        public static IList<XPathNavigator> ToNodeSet(XPathItem item)
        {
            return new XmlQueryNodeSequence(ToNode(item));
        }
 
        public static IList<XPathNavigator> ToNodeSet(IList<XPathItem> listItems)
        {
            XsltLibrary.CheckXsltValue(listItems);
 
            if (listItems.Count == 1)
                return new XmlQueryNodeSequence(ToNode(listItems[0]));
 
            return XmlILStorageConverter.ItemsToNavigators(listItems);
        }
 
 
        //------------------------------------------------------------------------
        // ToString (internal type to internal type)
        //------------------------------------------------------------------------
 
        public static string ToString(double value)
        {
            return XPathConvert.DoubleToString(value);
        }
 
        public static string ToString(XPathItem item)
        {
            XsltLibrary.CheckXsltValue(item);
 
            // Use XPath 1.0 rules to convert double to string
            if (!item.IsNode && item.ValueType == typeof(double))
                return XPathConvert.DoubleToString(item.ValueAsDouble);
 
            return item.Value;
        }
 
        public static string ToString(IList<XPathItem> listItems)
        {
            XsltLibrary.CheckXsltValue(listItems);
 
            if (listItems.Count == 0)
                return string.Empty;
 
            return ToString(listItems[0]);
        }
 
 
        //------------------------------------------------------------------------
        // External type to internal type
        //------------------------------------------------------------------------
 
        public static string ToString(DateTime value)
        {
            return (new XsdDateTime(value, XsdDateTimeFlags.DateTime)).ToString();
        }
 
        public static double ToDouble(decimal value)
        {
            return (double)value;
        }
 
        public static double ToDouble(int value)
        {
            return (double)value;
        }
 
        public static double ToDouble(long value)
        {
            return (double)value;
        }
 
 
        //------------------------------------------------------------------------
        // Internal type to external type
        //------------------------------------------------------------------------
 
        public static decimal ToDecimal(double value)
        {
            checked { return (decimal)value; }
        }
 
        public static int ToInt(double value)
        {
            checked { return (int)value; }
        }
 
        public static long ToLong(double value)
        {
            checked { return (long)value; }
        }
 
        public static DateTime ToDateTime(string value)
        {
            return (DateTime)(new XsdDateTime(value, XsdDateTimeFlags.AllXsd));
        }
 
 
        //------------------------------------------------------------------------
        // External type to external type
        //------------------------------------------------------------------------
 
        internal static XmlAtomicValue ConvertToType(XmlAtomicValue value, XmlQueryType destinationType)
        {
            Debug.Assert(destinationType.IsStrict && destinationType.IsAtomicValue, "Can only convert to strict atomic type.");
 
            // This conversion matrix should match the one in XmlILVisitor.GetXsltConvertMethod
            switch (destinationType.TypeCode)
            {
                case XmlTypeCode.Boolean:
                    switch (value.XmlType.TypeCode)
                    {
                        case XmlTypeCode.Boolean:
                        case XmlTypeCode.Double:
                        case XmlTypeCode.String:
                            return new XmlAtomicValue(destinationType.SchemaType, ToBoolean(value));
                    }
                    break;
 
                case XmlTypeCode.DateTime:
                    if (value.XmlType.TypeCode == XmlTypeCode.String)
                        return new XmlAtomicValue(destinationType.SchemaType, ToDateTime(value.Value));
                    break;
 
                case XmlTypeCode.Decimal:
                    if (value.XmlType.TypeCode == XmlTypeCode.Double)
                        return new XmlAtomicValue(destinationType.SchemaType, ToDecimal(value.ValueAsDouble));
                    break;
 
                case XmlTypeCode.Double:
                    switch (value.XmlType.TypeCode)
                    {
                        case XmlTypeCode.Boolean:
                        case XmlTypeCode.Double:
                        case XmlTypeCode.String:
                            return new XmlAtomicValue(destinationType.SchemaType, ToDouble(value));
 
                        case XmlTypeCode.Decimal:
                            return new XmlAtomicValue(destinationType.SchemaType, ToDouble((decimal)value.ValueAs(typeof(decimal), null)));
 
                        case XmlTypeCode.Int:
                        case XmlTypeCode.Long:
                            return new XmlAtomicValue(destinationType.SchemaType, ToDouble(value.ValueAsLong));
                    }
                    break;
 
                case XmlTypeCode.Int:
                case XmlTypeCode.Long:
                    if (value.XmlType.TypeCode == XmlTypeCode.Double)
                        return new XmlAtomicValue(destinationType.SchemaType, ToLong(value.ValueAsDouble));
                    break;
 
                case XmlTypeCode.String:
                    switch (value.XmlType.TypeCode)
                    {
                        case XmlTypeCode.Boolean:
                        case XmlTypeCode.Double:
                        case XmlTypeCode.String:
                            return new XmlAtomicValue(destinationType.SchemaType, ToString(value));
 
                        case XmlTypeCode.DateTime:
                            return new XmlAtomicValue(destinationType.SchemaType, ToString(value.ValueAsDateTime));
                    }
                    break;
            }
 
            Debug.Fail($"Conversion from {value.XmlType.QualifiedName.Name} to {destinationType} is not supported.");
            return value;
        }
 
 
        //------------------------------------------------------------------------
        // EnsureXXX methods (TreatAs)
        //------------------------------------------------------------------------
 
        public static IList<XPathNavigator> EnsureNodeSet(IList<XPathItem> listItems)
        {
            XsltLibrary.CheckXsltValue(listItems);
 
            if (listItems.Count == 1)
            {
                XPathItem item = listItems[0];
                if (!item.IsNode)
                    throw new XslTransformException(SR.XPath_NodeSetExpected, string.Empty);
 
                if (item is RtfNavigator)
                    throw new XslTransformException(SR.XPath_RtfInPathExpr, string.Empty);
            }
 
            return XmlILStorageConverter.ItemsToNavigators(listItems);
        }
 
 
        //------------------------------------------------------------------------
        // InferXsltType
        //------------------------------------------------------------------------
 
        /// <summary>
        /// Infer one of the Xslt types from "clrType" -- Boolean, Double, String, Node, Node*, Item*.
        /// </summary>
        internal static XmlQueryType InferXsltType(Type clrType)
        {
            if (clrType == typeof(bool)) return XmlQueryTypeFactory.BooleanX;
            if (clrType == typeof(byte)) return XmlQueryTypeFactory.DoubleX;
            if (clrType == typeof(decimal)) return XmlQueryTypeFactory.DoubleX;
            if (clrType == typeof(DateTime)) return XmlQueryTypeFactory.StringX;
            if (clrType == typeof(double)) return XmlQueryTypeFactory.DoubleX;
            if (clrType == typeof(short)) return XmlQueryTypeFactory.DoubleX;
            if (clrType == typeof(int)) return XmlQueryTypeFactory.DoubleX;
            if (clrType == typeof(long)) return XmlQueryTypeFactory.DoubleX;
            if (clrType == typeof(IXPathNavigable)) return XmlQueryTypeFactory.NodeNotRtf;
            if (clrType == typeof(sbyte)) return XmlQueryTypeFactory.DoubleX;
            if (clrType == typeof(float)) return XmlQueryTypeFactory.DoubleX;
            if (clrType == typeof(string)) return XmlQueryTypeFactory.StringX;
            if (clrType == typeof(ushort)) return XmlQueryTypeFactory.DoubleX;
            if (clrType == typeof(uint)) return XmlQueryTypeFactory.DoubleX;
            if (clrType == typeof(ulong)) return XmlQueryTypeFactory.DoubleX;
            if (clrType == typeof(XPathNavigator[])) return XmlQueryTypeFactory.NodeSDod;
            if (clrType == typeof(XPathNavigator)) return XmlQueryTypeFactory.NodeNotRtf;
            if (clrType == typeof(XPathNodeIterator)) return XmlQueryTypeFactory.NodeSDod;
            if (clrType.IsEnum) return XmlQueryTypeFactory.DoubleX;
            if (clrType == typeof(void)) return XmlQueryTypeFactory.Empty;
 
            return XmlQueryTypeFactory.ItemS;
        }
    }
}